1use std::alloc::alloc;
2use std::alloc::dealloc;
3use std::alloc::Layout;
4use std::ffi::CStr;
5use std::ffi::CString;
6use std::ffi::OsStr;
7use std::fmt::Debug;
8use std::io;
9use std::mem;
10use std::ops::Deref as _;
11use std::os::raw::c_char;
12use std::os::raw::c_void;
13use std::os::unix::ffi::OsStrExt as _;
14use std::path::Path;
15use std::path::PathBuf;
16use std::ptr;
17
18use blazesym::helper::ElfResolver;
19use blazesym::symbolize::cache;
20use blazesym::symbolize::source::Elf;
21use blazesym::symbolize::source::GsymData;
22use blazesym::symbolize::source::GsymFile;
23use blazesym::symbolize::source::Kernel;
24use blazesym::symbolize::source::Process;
25use blazesym::symbolize::source::Source;
26use blazesym::symbolize::CodeInfo;
27use blazesym::symbolize::Input;
28use blazesym::symbolize::ProcessMemberType;
29use blazesym::symbolize::Reason;
30use blazesym::symbolize::Sym;
31use blazesym::symbolize::Symbolized;
32use blazesym::symbolize::Symbolizer;
33use blazesym::Addr;
34use blazesym::MaybeDefault;
35
36use crate::blaze_err;
37#[cfg(doc)]
38use crate::blaze_err_last;
39use crate::set_last_err;
40use crate::util::slice_from_aligned_user_array;
41use crate::util::slice_from_user_array;
42use crate::util::DynSize as _;
43
44
45#[repr(C)]
47#[derive(Debug)]
48pub struct blaze_cache_src_elf {
49 pub type_size: usize,
54 pub path: *const c_char,
56 pub reserved: [u8; 16],
59}
60
61impl Default for blaze_cache_src_elf {
62 fn default() -> Self {
63 Self {
64 type_size: mem::size_of::<Self>(),
65 path: ptr::null(),
66 reserved: [0; 16],
67 }
68 }
69}
70
71impl From<blaze_cache_src_elf> for cache::Elf {
72 fn from(elf: blaze_cache_src_elf) -> Self {
73 let blaze_cache_src_elf {
74 type_size: _,
75 path,
76 reserved: _,
77 } = elf;
78 Self {
79 path: unsafe { from_cstr(path) },
80 _non_exhaustive: (),
81 }
82 }
83}
84
85
86#[repr(C)]
88#[derive(Debug)]
89pub struct blaze_cache_src_process {
90 pub type_size: usize,
95 pub pid: u32,
97 pub cache_vmas: bool,
116 pub reserved: [u8; 19],
119}
120
121impl Default for blaze_cache_src_process {
122 fn default() -> Self {
123 Self {
124 type_size: mem::size_of::<Self>(),
125 pid: 0,
126 cache_vmas: false,
127 reserved: [0; 19],
128 }
129 }
130}
131
132impl From<blaze_cache_src_process> for cache::Process {
133 fn from(process: blaze_cache_src_process) -> Self {
134 let blaze_cache_src_process {
135 type_size: _,
136 pid,
137 cache_vmas,
138 reserved: _,
139 } = process;
140 Self {
141 pid: pid.into(),
142 cache_vmas,
143 _non_exhaustive: (),
144 }
145 }
146}
147
148
149#[repr(C)]
154#[derive(Debug)]
155pub struct blaze_symbolize_src_elf {
156 pub type_size: usize,
161 pub path: *const c_char,
168 pub debug_syms: bool,
171 pub reserved: [u8; 23],
174}
175
176impl Default for blaze_symbolize_src_elf {
177 fn default() -> Self {
178 Self {
179 type_size: mem::size_of::<Self>(),
180 path: ptr::null(),
181 debug_syms: false,
182 reserved: [0; 23],
183 }
184 }
185}
186
187impl From<blaze_symbolize_src_elf> for Elf {
188 fn from(elf: blaze_symbolize_src_elf) -> Self {
189 let blaze_symbolize_src_elf {
190 type_size: _,
191 path,
192 debug_syms,
193 reserved: _,
194 } = elf;
195 Self {
196 path: unsafe { from_cstr(path) },
197 debug_syms,
198 _non_exhaustive: (),
199 }
200 }
201}
202
203
204#[repr(C)]
206#[derive(Debug)]
207pub struct blaze_symbolize_src_kernel {
208 pub type_size: usize,
213 pub kallsyms: *const c_char,
223 pub vmlinux: *const c_char,
239 pub debug_syms: bool,
242 pub reserved: [u8; 23],
245}
246
247impl Default for blaze_symbolize_src_kernel {
248 fn default() -> Self {
249 Self {
250 type_size: mem::size_of::<Self>(),
251 kallsyms: ptr::null(),
252 vmlinux: ptr::null(),
253 debug_syms: false,
254 reserved: [0; 23],
255 }
256 }
257}
258
259impl From<blaze_symbolize_src_kernel> for Kernel {
260 fn from(kernel: blaze_symbolize_src_kernel) -> Self {
261 fn to_maybe_path(path: *const c_char) -> MaybeDefault<PathBuf> {
262 if !path.is_null() {
263 let path = unsafe { from_cstr(path) };
264 if path.as_os_str().is_empty() {
265 MaybeDefault::None
266 } else {
267 MaybeDefault::Some(path)
268 }
269 } else {
270 MaybeDefault::Default
271 }
272 }
273
274 let blaze_symbolize_src_kernel {
275 type_size: _,
276 kallsyms,
277 vmlinux,
278 debug_syms,
279 reserved: _,
280 } = kernel;
281 Self {
282 kallsyms: to_maybe_path(kallsyms),
283 vmlinux: to_maybe_path(vmlinux),
284 kaslr_offset: None,
285 debug_syms,
286 _non_exhaustive: (),
287 }
288 }
289}
290
291
292#[repr(C)]
297#[derive(Debug)]
298pub struct blaze_symbolize_src_process {
299 pub type_size: usize,
304 pub pid: u32,
306 pub debug_syms: bool,
309 pub perf_map: bool,
312 pub no_map_files: bool,
321 pub no_vdso: bool,
324 pub reserved: [u8; 16],
327}
328
329impl Default for blaze_symbolize_src_process {
330 fn default() -> Self {
331 Self {
332 type_size: mem::size_of::<Self>(),
333 pid: 0,
334 debug_syms: false,
335 perf_map: false,
336 no_map_files: false,
337 no_vdso: false,
338 reserved: [0; 16],
339 }
340 }
341}
342
343impl From<blaze_symbolize_src_process> for Process {
344 fn from(process: blaze_symbolize_src_process) -> Self {
345 let blaze_symbolize_src_process {
346 type_size: _,
347 pid,
348 debug_syms,
349 perf_map,
350 no_map_files,
351 no_vdso,
352 reserved: _,
353 } = process;
354 Self {
355 pid: pid.into(),
356 debug_syms,
357 perf_map,
358 map_files: !no_map_files,
359 vdso: !no_vdso,
360 _non_exhaustive: (),
361 }
362 }
363}
364
365
366#[repr(C)]
368#[derive(Debug)]
369pub struct blaze_symbolize_src_gsym_data {
370 pub type_size: usize,
375 pub data: *const u8,
377 pub data_len: usize,
379 pub reserved: [u8; 16],
382}
383
384impl Default for blaze_symbolize_src_gsym_data {
385 fn default() -> Self {
386 Self {
387 type_size: mem::size_of::<Self>(),
388 data: ptr::null(),
389 data_len: 0,
390 reserved: [0; 16],
391 }
392 }
393}
394
395impl From<blaze_symbolize_src_gsym_data> for GsymData<'_> {
396 fn from(gsym: blaze_symbolize_src_gsym_data) -> Self {
397 let blaze_symbolize_src_gsym_data {
398 type_size: _,
399 data,
400 data_len,
401 reserved: _,
402 } = gsym;
403 Self {
404 data: unsafe { slice_from_aligned_user_array(data, data_len) },
405 _non_exhaustive: (),
406 }
407 }
408}
409
410
411#[repr(C)]
413#[derive(Debug)]
414pub struct blaze_symbolize_src_gsym_file {
415 pub type_size: usize,
420 pub path: *const c_char,
422 pub reserved: [u8; 16],
425}
426
427impl Default for blaze_symbolize_src_gsym_file {
428 fn default() -> Self {
429 Self {
430 type_size: mem::size_of::<Self>(),
431 path: ptr::null(),
432 reserved: [0; 16],
433 }
434 }
435}
436
437impl From<blaze_symbolize_src_gsym_file> for GsymFile {
438 fn from(gsym: blaze_symbolize_src_gsym_file) -> Self {
439 let blaze_symbolize_src_gsym_file {
440 type_size: _,
441 path,
442 reserved: _,
443 } = gsym;
444 Self {
445 path: unsafe { from_cstr(path) },
446 _non_exhaustive: (),
447 }
448 }
449}
450
451
452pub type blaze_symbolizer = Symbolizer;
457
458
459#[repr(transparent)]
465#[derive(Copy, Clone, Debug, Eq, PartialEq)]
466pub struct blaze_symbolize_reason(u8);
467
468impl blaze_symbolize_reason {
471 pub const SUCCESS: Self = Self(0);
473 pub const UNMAPPED: Self = Self(1);
476 pub const INVALID_FILE_OFFSET: Self = Self(2);
478 pub const MISSING_COMPONENT: Self = Self(3);
482 pub const MISSING_SYMS: Self = Self(4);
484 pub const UNKNOWN_ADDR: Self = Self(5);
486 pub const UNSUPPORTED: Self = Self(6);
488 pub const IGNORED_ERROR: Self = Self(7);
490}
491
492
493impl From<Reason> for blaze_symbolize_reason {
494 fn from(reason: Reason) -> Self {
495 match reason {
496 Reason::Unmapped => Self::UNMAPPED,
497 Reason::InvalidFileOffset => Self::INVALID_FILE_OFFSET,
498 Reason::MissingComponent => Self::MISSING_COMPONENT,
499 Reason::MissingSyms => Self::MISSING_SYMS,
500 Reason::Unsupported => Self::UNSUPPORTED,
501 Reason::UnknownAddr => Self::UNKNOWN_ADDR,
502 Reason::IgnoredError => Self::IGNORED_ERROR,
503 _ => unreachable!(),
504 }
505 }
506}
507
508
509#[no_mangle]
512pub extern "C" fn blaze_symbolize_reason_str(reason: blaze_symbolize_reason) -> *const c_char {
513 match reason {
514 blaze_symbolize_reason::SUCCESS => b"success\0".as_ptr().cast(),
515 blaze_symbolize_reason::UNMAPPED => Reason::Unmapped.as_bytes().as_ptr().cast(),
516 blaze_symbolize_reason::INVALID_FILE_OFFSET => {
517 Reason::InvalidFileOffset.as_bytes().as_ptr().cast()
518 }
519 blaze_symbolize_reason::MISSING_COMPONENT => {
520 Reason::MissingComponent.as_bytes().as_ptr().cast()
521 }
522 blaze_symbolize_reason::MISSING_SYMS => Reason::MissingSyms.as_bytes().as_ptr().cast(),
523 blaze_symbolize_reason::UNKNOWN_ADDR => Reason::UnknownAddr.as_bytes().as_ptr().cast(),
524 blaze_symbolize_reason::UNSUPPORTED => Reason::Unsupported.as_bytes().as_ptr().cast(),
525 blaze_symbolize_reason::IGNORED_ERROR => Reason::IgnoredError.as_bytes().as_ptr().cast(),
526 _ => b"unknown reason\0".as_ptr().cast(),
527 }
528}
529
530
531#[repr(C)]
533#[derive(Debug)]
534pub struct blaze_symbolize_code_info {
535 pub dir: *const c_char,
539 pub file: *const c_char,
543 pub line: u32,
546 pub column: u16,
549 pub reserved: [u8; 10],
551}
552
553
554#[repr(C)]
556#[derive(Debug)]
557pub struct blaze_symbolize_inlined_fn {
558 pub name: *const c_char,
560 pub code_info: blaze_symbolize_code_info,
562 pub reserved: [u8; 8],
564}
565
566
567#[repr(C)]
572#[derive(Debug)]
573pub struct blaze_sym {
574 pub name: *const c_char,
580 pub module: *const c_char,
589 pub addr: Addr,
595 pub offset: usize,
605 pub size: isize,
612 pub code_info: blaze_symbolize_code_info,
614 pub inlined_cnt: usize,
616 pub inlined: *const blaze_symbolize_inlined_fn,
618 pub reason: blaze_symbolize_reason,
621 pub reserved: [u8; 15],
623}
624
625#[repr(C)]
630#[derive(Debug)]
631pub struct blaze_syms {
632 pub cnt: usize,
634 pub syms: [blaze_sym; 0],
639}
640
641pub(crate) unsafe fn from_cstr(cstr: *const c_char) -> PathBuf {
646 Path::new(OsStr::from_bytes(
647 unsafe { CStr::from_ptr(cstr) }.to_bytes(),
648 ))
649 .to_path_buf()
650}
651
652
653#[repr(C)]
671#[derive(Debug)]
672pub struct blaze_symbolizer_dispatch {
673 pub dispatch_cb: unsafe extern "C" fn(
675 maps_file: *const c_char,
676 symbolic_path: *const c_char,
677 ctx: *mut c_void,
678 ) -> *mut c_char,
679 pub ctx: *mut c_void,
681}
682
683#[repr(C)]
685#[derive(Debug)]
686pub struct blaze_symbolizer_opts {
687 pub type_size: usize,
692 pub debug_dirs: *const *const c_char,
703 pub debug_dirs_len: usize,
705 pub auto_reload: bool,
709 pub code_info: bool,
716 pub inlined_fns: bool,
721 pub demangle: bool,
727 pub _reserved1: [u8; 4],
730 pub process_dispatch: *const blaze_symbolizer_dispatch,
733 pub reserved: [u8; 8],
736}
737
738impl Default for blaze_symbolizer_opts {
739 fn default() -> Self {
740 Self {
741 type_size: mem::size_of::<Self>(),
742 debug_dirs: ptr::null(),
743 debug_dirs_len: 0,
744 auto_reload: false,
745 code_info: false,
746 inlined_fns: false,
747 demangle: false,
748 _reserved1: [0; 4],
749 process_dispatch: ptr::null(),
750 reserved: [0; 8],
751 }
752 }
753}
754
755
756#[no_mangle]
769pub extern "C" fn blaze_symbolizer_new() -> *mut blaze_symbolizer {
770 let symbolizer = Symbolizer::new();
771 let symbolizer_box = Box::new(symbolizer);
772 let () = set_last_err(blaze_err::OK);
773 Box::into_raw(symbolizer_box)
774}
775
776#[no_mangle]
789pub unsafe extern "C" fn blaze_symbolizer_new_opts(
790 opts: *const blaze_symbolizer_opts,
791) -> *mut blaze_symbolizer {
792 if !input_zeroed!(opts, blaze_symbolizer_opts) {
793 let () = set_last_err(blaze_err::INVALID_INPUT);
794 return ptr::null_mut()
795 }
796 let opts = input_sanitize!(opts, blaze_symbolizer_opts);
797
798 let blaze_symbolizer_opts {
799 type_size: _,
800 debug_dirs,
801 debug_dirs_len,
802 auto_reload,
803 code_info,
804 inlined_fns,
805 demangle,
806 _reserved1: _,
807 process_dispatch,
808 reserved: _,
809 } = opts;
810
811 let builder = Symbolizer::builder()
812 .enable_auto_reload(auto_reload)
813 .enable_code_info(code_info)
814 .enable_inlined_fns(inlined_fns)
815 .enable_demangling(demangle);
816
817 let debug_dir_paths = if debug_dirs.is_null() {
818 None
819 } else {
820 let slice = unsafe { slice_from_user_array(debug_dirs, debug_dirs_len) };
823 Some(
824 slice
825 .iter()
826 .map(|cstr| {
827 PathBuf::from(OsStr::from_bytes(
828 unsafe { CStr::from_ptr(cstr.cast()) }.to_bytes(),
831 ))
832 })
833 .collect::<Vec<_>>(),
834 )
835 };
836
837 #[cfg(feature = "dwarf")]
838 let builder = if let Some(ref dirs) = debug_dir_paths {
839 builder.set_debug_dirs(Some(dirs.iter().map(PathBuf::as_path)))
840 } else {
841 builder
842 };
843
844 let builder = if !process_dispatch.is_null() {
845 let dispatch = unsafe { &*process_dispatch };
847 let cb = dispatch.dispatch_cb;
848 let ctx = dispatch.ctx;
849 builder.set_process_dispatcher(move |info| {
850 match info.member_entry {
851 ProcessMemberType::Path(entry) => {
852 let maps_file = CString::new(entry.maps_file.as_os_str().as_bytes())
853 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
854 let sym_path = CString::new(entry.symbolic_path.as_os_str().as_bytes())
855 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
856 let result = unsafe { cb(maps_file.as_ptr(), sym_path.as_ptr(), ctx) };
860 if result.is_null() {
861 return Ok(None)
862 }
863 let path_cstr = unsafe { CStr::from_ptr(result) };
866 let path = Path::new(OsStr::from_bytes(path_cstr.to_bytes()));
867 let resolver = if let Some(ref dirs) = debug_dir_paths {
868 ElfResolver::open_with_debug_dirs(path, dirs)
869 } else {
870 ElfResolver::open(path)
871 };
872 unsafe { libc::free(result.cast()) };
874 Ok(Some(Box::new(resolver?)))
875 }
876 _ => Ok(None),
877 }
878 })
879 } else {
880 builder
881 };
882
883 let symbolizer = builder.build();
884 let symbolizer_box = Box::new(symbolizer);
885 let () = set_last_err(blaze_err::OK);
886 Box::into_raw(symbolizer_box)
887}
888
889#[no_mangle]
895pub unsafe extern "C" fn blaze_symbolizer_free(symbolizer: *mut blaze_symbolizer) {
896 if !symbolizer.is_null() {
897 drop(unsafe { Box::from_raw(symbolizer) });
898 }
899}
900
901
902#[no_mangle]
915pub unsafe extern "C" fn blaze_symbolize_cache_elf(
916 symbolizer: *mut blaze_symbolizer,
917 cache: *const blaze_cache_src_elf,
918) {
919 if !input_zeroed!(cache, blaze_cache_src_elf) {
920 let () = set_last_err(blaze_err::INVALID_INPUT);
921 return
922 }
923 let cache = input_sanitize!(cache, blaze_cache_src_elf);
924 let cache = cache::Cache::from(cache::Elf::from(cache));
925
926 let symbolizer = unsafe { &*symbolizer };
928 let result = symbolizer.cache(&cache);
929 let err = result
930 .map(|()| blaze_err::OK)
931 .unwrap_or_else(|err| err.kind().into());
932 let () = set_last_err(err);
933}
934
935
936#[no_mangle]
956pub unsafe extern "C" fn blaze_symbolize_cache_process(
957 symbolizer: *mut blaze_symbolizer,
958 cache: *const blaze_cache_src_process,
959) {
960 if !input_zeroed!(cache, blaze_cache_src_process) {
961 let () = set_last_err(blaze_err::INVALID_INPUT);
962 return
963 }
964 let cache = input_sanitize!(cache, blaze_cache_src_process);
965 let cache = cache::Cache::from(cache::Process::from(cache));
966
967 let symbolizer = unsafe { &*symbolizer };
969 let result = symbolizer.cache(&cache);
970 let err = result
971 .map(|()| blaze_err::OK)
972 .unwrap_or_else(|err| err.kind().into());
973 let () = set_last_err(err);
974}
975
976fn make_cstr(src: &OsStr, cstr_last: &mut *mut c_char) -> *mut c_char {
977 let cstr = *cstr_last;
978 unsafe { ptr::copy_nonoverlapping(src.as_bytes().as_ptr(), cstr as *mut u8, src.len()) };
979 unsafe { *cstr.add(src.len()) = 0 };
980 *cstr_last = unsafe { cstr_last.add(src.len() + 1) };
981
982 cstr
983}
984
985fn convert_code_info(
986 code_info_in: Option<&CodeInfo>,
987 code_info_out: &mut blaze_symbolize_code_info,
988 cstr_last: &mut *mut c_char,
989) {
990 code_info_out.dir = code_info_in
991 .as_ref()
992 .and_then(|info| {
993 info.dir
994 .as_ref()
995 .map(|d| make_cstr(d.as_os_str(), cstr_last))
996 })
997 .unwrap_or_else(ptr::null_mut);
998 code_info_out.file = code_info_in
999 .as_ref()
1000 .map(|info| make_cstr(&info.file, cstr_last))
1001 .unwrap_or_else(ptr::null_mut);
1002 code_info_out.line = code_info_in
1003 .as_ref()
1004 .and_then(|info| info.line)
1005 .unwrap_or(0);
1006 code_info_out.column = code_info_in
1007 .as_ref()
1008 .and_then(|info| info.column)
1009 .unwrap_or(0);
1010}
1011
1012
1013pub(crate) fn convert_sym(
1015 sym: &Sym,
1016 sym_ref: &mut blaze_sym,
1017 inlined_last: &mut *mut blaze_symbolize_inlined_fn,
1018 cstr_last: &mut *mut c_char,
1019) {
1020 let name_ptr = make_cstr(OsStr::new(sym.name.as_ref()), cstr_last);
1021 let module_ptr = sym
1022 .module
1023 .as_deref()
1024 .map(|module| make_cstr(module, cstr_last))
1025 .unwrap_or(ptr::null_mut());
1026
1027 sym_ref.name = name_ptr;
1028 sym_ref.module = module_ptr;
1029 sym_ref.addr = sym.addr;
1030 sym_ref.offset = sym.offset;
1031 sym_ref.size = sym
1032 .size
1033 .map(|size| isize::try_from(size).unwrap_or(isize::MAX))
1034 .unwrap_or(-1);
1035 convert_code_info(sym.code_info.as_deref(), &mut sym_ref.code_info, cstr_last);
1036 sym_ref.inlined_cnt = sym.inlined.len();
1037 sym_ref.inlined = *inlined_last;
1038 sym_ref.reason = blaze_symbolize_reason::SUCCESS;
1039
1040 for inlined in sym.inlined.iter() {
1041 let inlined_ref = unsafe { &mut **inlined_last };
1042
1043 let name_ptr = make_cstr(OsStr::new(inlined.name.as_ref()), cstr_last);
1044 inlined_ref.name = name_ptr;
1045 convert_code_info(
1046 inlined.code_info.as_ref(),
1047 &mut inlined_ref.code_info,
1048 cstr_last,
1049 );
1050
1051 *inlined_last = unsafe { inlined_last.add(1) };
1052 }
1053}
1054
1055
1056fn convert_symbolizedresults_to_c(results: Vec<Symbolized>) -> *const blaze_syms {
1061 let (strtab_size, inlined_fn_cnt) = results.iter().fold((0, 0), |acc, sym| match sym {
1064 Symbolized::Sym(sym) => (acc.0 + sym.c_str_size(), acc.1 + sym.inlined.len()),
1065 Symbolized::Unknown(..) => acc,
1066 });
1067
1068 let buf_size = mem::size_of::<u64>()
1069 + strtab_size
1070 + mem::size_of::<blaze_syms>()
1071 + mem::size_of::<blaze_sym>() * results.len()
1072 + mem::size_of::<blaze_symbolize_inlined_fn>() * inlined_fn_cnt;
1073 let buf = unsafe { alloc(Layout::from_size_align(buf_size, 8).unwrap()) };
1074 if buf.is_null() {
1075 return ptr::null()
1076 }
1077
1078 unsafe { *buf.cast::<u64>() = buf_size as u64 };
1080
1081 let syms_buf = unsafe { buf.add(mem::size_of::<u64>()) };
1082
1083 let syms_ptr = syms_buf.cast::<blaze_syms>();
1084 let mut syms_last = unsafe { (*syms_ptr).syms.as_mut_ptr() };
1085 let mut inlined_last = unsafe {
1086 syms_buf.add(mem::size_of::<blaze_syms>() + mem::size_of::<blaze_sym>() * results.len())
1087 } as *mut blaze_symbolize_inlined_fn;
1088 let mut cstr_last = unsafe {
1089 syms_buf.add(
1090 mem::size_of::<blaze_syms>()
1091 + mem::size_of::<blaze_sym>() * results.len()
1092 + mem::size_of::<blaze_symbolize_inlined_fn>() * inlined_fn_cnt,
1093 )
1094 } as *mut c_char;
1095
1096 unsafe { (*syms_ptr).cnt = results.len() };
1097
1098 for sym in results {
1100 match sym {
1101 Symbolized::Sym(sym) => {
1102 let sym_ref = unsafe { &mut *syms_last };
1103 let () = convert_sym(&sym, sym_ref, &mut inlined_last, &mut cstr_last);
1104 }
1105 Symbolized::Unknown(reason) => {
1106 let () = unsafe { syms_last.write_bytes(0, 1) };
1111 let sym_ref = unsafe { &mut *syms_last };
1112 sym_ref.reason = reason.into();
1113 }
1114 }
1115
1116 syms_last = unsafe { syms_last.add(1) };
1117 }
1118
1119 syms_ptr
1120}
1121
1122unsafe fn blaze_symbolize_impl(
1123 symbolizer: *mut blaze_symbolizer,
1124 src: Source<'_>,
1125 inputs: Input<*const u64>,
1126 input_cnt: usize,
1127) -> *const blaze_syms {
1128 let symbolizer = unsafe { &*symbolizer };
1130 let addrs = unsafe { slice_from_user_array(*inputs.as_inner_ref(), input_cnt) };
1133
1134 let input = match inputs {
1135 Input::AbsAddr(..) => Input::AbsAddr(addrs.deref()),
1136 Input::VirtOffset(..) => Input::VirtOffset(addrs.deref()),
1137 Input::FileOffset(..) => Input::FileOffset(addrs.deref()),
1138 };
1139
1140 let result = symbolizer.symbolize(&src, input);
1141 match result {
1142 Ok(results) if results.is_empty() => {
1143 let () = set_last_err(blaze_err::OK);
1144 ptr::null()
1145 }
1146 Ok(results) => {
1147 let result = convert_symbolizedresults_to_c(results);
1148 if result.is_null() {
1149 let () = set_last_err(blaze_err::OUT_OF_MEMORY);
1150 } else {
1151 let () = set_last_err(blaze_err::OK);
1152 }
1153 result
1154 }
1155 Err(err) => {
1156 let () = set_last_err(err.kind().into());
1157 ptr::null_mut()
1158 }
1159 }
1160}
1161
1162
1163#[no_mangle]
1179pub unsafe extern "C" fn blaze_symbolize_process_abs_addrs(
1180 symbolizer: *mut blaze_symbolizer,
1181 src: *const blaze_symbolize_src_process,
1182 abs_addrs: *const Addr,
1183 abs_addr_cnt: usize,
1184) -> *const blaze_syms {
1185 if !input_zeroed!(src, blaze_symbolize_src_process) {
1186 let () = set_last_err(blaze_err::INVALID_INPUT);
1187 return ptr::null()
1188 }
1189 let src = input_sanitize!(src, blaze_symbolize_src_process);
1190 let src = Source::from(Process::from(src));
1191
1192 unsafe { blaze_symbolize_impl(symbolizer, src, Input::AbsAddr(abs_addrs), abs_addr_cnt) }
1193}
1194
1195
1196#[no_mangle]
1212pub unsafe extern "C" fn blaze_symbolize_kernel_abs_addrs(
1213 symbolizer: *mut blaze_symbolizer,
1214 src: *const blaze_symbolize_src_kernel,
1215 abs_addrs: *const Addr,
1216 abs_addr_cnt: usize,
1217) -> *const blaze_syms {
1218 if !input_zeroed!(src, blaze_symbolize_src_kernel) {
1219 let () = set_last_err(blaze_err::INVALID_INPUT);
1220 return ptr::null()
1221 }
1222 let src = input_sanitize!(src, blaze_symbolize_src_kernel);
1223 let src = Source::from(Kernel::from(src));
1224
1225 unsafe { blaze_symbolize_impl(symbolizer, src, Input::AbsAddr(abs_addrs), abs_addr_cnt) }
1226}
1227
1228
1229#[no_mangle]
1245pub unsafe extern "C" fn blaze_symbolize_elf_virt_offsets(
1246 symbolizer: *mut blaze_symbolizer,
1247 src: *const blaze_symbolize_src_elf,
1248 virt_offsets: *const Addr,
1249 virt_offset_cnt: usize,
1250) -> *const blaze_syms {
1251 if !input_zeroed!(src, blaze_symbolize_src_elf) {
1252 let () = set_last_err(blaze_err::INVALID_INPUT);
1253 return ptr::null()
1254 }
1255 let src = input_sanitize!(src, blaze_symbolize_src_elf);
1256 let src = Source::from(Elf::from(src));
1257
1258 unsafe {
1259 blaze_symbolize_impl(
1260 symbolizer,
1261 src,
1262 Input::VirtOffset(virt_offsets),
1263 virt_offset_cnt,
1264 )
1265 }
1266}
1267
1268#[no_mangle]
1284pub unsafe extern "C" fn blaze_symbolize_elf_file_offsets(
1285 symbolizer: *mut blaze_symbolizer,
1286 src: *const blaze_symbolize_src_elf,
1287 file_offsets: *const Addr,
1288 file_offset_cnt: usize,
1289) -> *const blaze_syms {
1290 if !input_zeroed!(src, blaze_symbolize_src_elf) {
1291 let () = set_last_err(blaze_err::INVALID_INPUT);
1292 return ptr::null()
1293 }
1294 let src = input_sanitize!(src, blaze_symbolize_src_elf);
1295 let src = Source::from(Elf::from(src));
1296
1297 unsafe {
1298 blaze_symbolize_impl(
1299 symbolizer,
1300 src,
1301 Input::FileOffset(file_offsets),
1302 file_offset_cnt,
1303 )
1304 }
1305}
1306
1307
1308#[no_mangle]
1324pub unsafe extern "C" fn blaze_symbolize_gsym_data_virt_offsets(
1325 symbolizer: *mut blaze_symbolizer,
1326 src: *const blaze_symbolize_src_gsym_data,
1327 virt_offsets: *const Addr,
1328 virt_offset_cnt: usize,
1329) -> *const blaze_syms {
1330 if !input_zeroed!(src, blaze_symbolize_src_gsym_data) {
1331 let () = set_last_err(blaze_err::INVALID_INPUT);
1332 return ptr::null()
1333 }
1334 let src = input_sanitize!(src, blaze_symbolize_src_gsym_data);
1335 let src = Source::from(GsymData::from(src));
1336 unsafe {
1337 blaze_symbolize_impl(
1338 symbolizer,
1339 src,
1340 Input::VirtOffset(virt_offsets),
1341 virt_offset_cnt,
1342 )
1343 }
1344}
1345
1346
1347#[no_mangle]
1363pub unsafe extern "C" fn blaze_symbolize_gsym_file_virt_offsets(
1364 symbolizer: *mut blaze_symbolizer,
1365 src: *const blaze_symbolize_src_gsym_file,
1366 virt_offsets: *const Addr,
1367 virt_offset_cnt: usize,
1368) -> *const blaze_syms {
1369 if !input_zeroed!(src, blaze_symbolize_src_gsym_file) {
1370 let () = set_last_err(blaze_err::INVALID_INPUT);
1371 return ptr::null()
1372 }
1373 let src = input_sanitize!(src, blaze_symbolize_src_gsym_file);
1374 let src = Source::from(GsymFile::from(src));
1375
1376 unsafe {
1377 blaze_symbolize_impl(
1378 symbolizer,
1379 src,
1380 Input::VirtOffset(virt_offsets),
1381 virt_offset_cnt,
1382 )
1383 }
1384}
1385
1386
1387#[no_mangle]
1393pub unsafe extern "C" fn blaze_syms_free(syms: *const blaze_syms) {
1394 if syms.is_null() {
1395 return
1396 }
1397
1398 let buf = unsafe { syms.byte_sub(mem::size_of::<u64>()).cast::<u8>().cast_mut() };
1400 let size = unsafe { *buf.cast::<u64>() } as usize;
1401 unsafe { dealloc(buf, Layout::from_size_align(size, 8).unwrap()) };
1402}
1403
1404
1405#[cfg(test)]
1406mod tests {
1407 use super::*;
1408
1409 use std::borrow::Cow;
1410 use std::ffi::CString;
1411 use std::fs::copy;
1412 use std::fs::read as read_file;
1413 use std::fs::remove_file;
1414 use std::hint::black_box;
1415 use std::io::Error;
1416 use std::os::unix::ffi::OsStringExt as _;
1417 use std::slice;
1418
1419 use blazesym::inspect;
1420 use blazesym::normalize;
1421 use blazesym::symbolize::InlinedFn;
1422 use blazesym::symbolize::Reason;
1423 use blazesym::Pid;
1424
1425 use tempfile::tempdir;
1426
1427 use test_tag::tag;
1428
1429 use crate::blaze_err_last;
1430
1431
1432 #[tag(miri)]
1434 #[test]
1435 #[cfg(target_pointer_width = "64")]
1436 fn type_sizes() {
1437 assert_eq!(mem::size_of::<blaze_cache_src_elf>(), 32);
1438 assert_eq!(mem::size_of::<blaze_cache_src_process>(), 32);
1439 assert_eq!(mem::size_of::<blaze_symbolize_src_elf>(), 40);
1440 assert_eq!(mem::size_of::<blaze_symbolize_src_kernel>(), 48);
1441 assert_eq!(mem::size_of::<blaze_symbolize_src_process>(), 32);
1442 assert_eq!(mem::size_of::<blaze_symbolize_src_gsym_data>(), 40);
1443 assert_eq!(mem::size_of::<blaze_symbolize_src_gsym_file>(), 32);
1444 assert_eq!(mem::size_of::<blaze_symbolizer_opts>(), 48);
1445 assert_eq!(mem::size_of::<blaze_symbolize_code_info>(), 32);
1446 assert_eq!(mem::size_of::<blaze_symbolize_inlined_fn>(), 48);
1447 assert_eq!(mem::size_of::<blaze_sym>(), 104);
1448 }
1449
1450 #[tag(miri)]
1452 #[test]
1453 fn debug_repr() {
1454 let elf = blaze_symbolize_src_elf {
1455 type_size: 24,
1456 ..Default::default()
1457 };
1458 assert_eq!(
1459 format!("{elf:?}"),
1460 "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] }"
1461 );
1462
1463 let kernel = blaze_symbolize_src_kernel {
1464 type_size: 32,
1465 debug_syms: true,
1466 ..Default::default()
1467 };
1468 assert_eq!(
1469 format!("{kernel:?}"),
1470 "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] }"
1471 );
1472
1473 let process = blaze_symbolize_src_process {
1474 type_size: 16,
1475 pid: 1337,
1476 debug_syms: true,
1477 ..Default::default()
1478 };
1479 assert_eq!(
1480 format!("{process:?}"),
1481 "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] }"
1482 );
1483
1484 let gsym_data = blaze_symbolize_src_gsym_data {
1485 type_size: 24,
1486 data: ptr::null(),
1487 data_len: 0,
1488 reserved: [0; 16],
1489 };
1490 assert_eq!(
1491 format!("{gsym_data:?}"),
1492 "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] }"
1493 );
1494
1495 let gsym_file = blaze_symbolize_src_gsym_file {
1496 type_size: 16,
1497 path: ptr::null(),
1498 reserved: [0; 16],
1499 };
1500 assert_eq!(
1501 format!("{gsym_file:?}"),
1502 "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] }"
1503 );
1504
1505 let sym = blaze_sym {
1506 name: ptr::null(),
1507 module: ptr::null(),
1508 addr: 0x1337,
1509 offset: 24,
1510 size: 16,
1511 code_info: blaze_symbolize_code_info {
1512 dir: ptr::null(),
1513 file: ptr::null(),
1514 line: 42,
1515 column: 1,
1516 reserved: [0; 10],
1517 },
1518 inlined_cnt: 0,
1519 inlined: ptr::null(),
1520 reason: blaze_symbolize_reason::UNSUPPORTED,
1521 reserved: [0; 15],
1522 };
1523 assert_eq!(
1524 format!("{sym:?}"),
1525 "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] }"
1526 );
1527
1528 let inlined = blaze_symbolize_inlined_fn {
1529 name: ptr::null(),
1530 code_info: blaze_symbolize_code_info {
1531 dir: ptr::null(),
1532 file: ptr::null(),
1533 line: 42,
1534 column: 1,
1535 reserved: [0; 10],
1536 },
1537 reserved: [0; 8],
1538 };
1539 assert_eq!(
1540 format!("{inlined:?}"),
1541 "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] }"
1542 );
1543
1544 let syms = blaze_syms { cnt: 0, syms: [] };
1545 assert_eq!(format!("{syms:?}"), "blaze_syms { cnt: 0, syms: [] }");
1546
1547 let opts = blaze_symbolizer_opts {
1548 type_size: 16,
1549 demangle: true,
1550 ..Default::default()
1551 };
1552 assert_eq!(
1553 format!("{opts:?}"),
1554 "blaze_symbolizer_opts { type_size: 16, debug_dirs: 0x0, debug_dirs_len: 0, auto_reload: false, code_info: false, inlined_fns: false, demangle: true, _reserved1: [0, 0, 0, 0], process_dispatch: 0x0, reserved: [0, 0, 0, 0, 0, 0, 0, 0] }"
1555 );
1556 }
1557
1558 #[tag(miri)]
1560 #[test]
1561 fn reason_stringification() {
1562 let data = [
1563 (Reason::Unmapped, blaze_symbolize_reason::UNMAPPED),
1564 (
1565 Reason::InvalidFileOffset,
1566 blaze_symbolize_reason::INVALID_FILE_OFFSET,
1567 ),
1568 (
1569 Reason::MissingComponent,
1570 blaze_symbolize_reason::MISSING_COMPONENT,
1571 ),
1572 (Reason::MissingSyms, blaze_symbolize_reason::MISSING_SYMS),
1573 (Reason::Unsupported, blaze_symbolize_reason::UNSUPPORTED),
1574 (Reason::UnknownAddr, blaze_symbolize_reason::UNKNOWN_ADDR),
1575 (Reason::IgnoredError, blaze_symbolize_reason::IGNORED_ERROR),
1576 ];
1577
1578 for (reason, expected) in data {
1579 assert_eq!(blaze_symbolize_reason::from(reason), expected);
1580 let cstr = unsafe { CStr::from_ptr(blaze_symbolize_reason_str(expected)) };
1581 let expected = CStr::from_bytes_with_nul(reason.as_bytes()).unwrap();
1582 assert_eq!(cstr, expected);
1583 }
1584 }
1585
1586
1587 #[tag(miri)]
1590 #[test]
1591 fn kernel_conversion() {
1592 let kernel = blaze_symbolize_src_kernel::default();
1593 let kernel = Kernel::from(kernel);
1594 assert_eq!(kernel.kallsyms, MaybeDefault::Default);
1595 assert_eq!(kernel.vmlinux, MaybeDefault::Default);
1596
1597 let kernel = blaze_symbolize_src_kernel {
1598 kallsyms: b"\0" as *const _ as *const c_char,
1599 vmlinux: b"\0" as *const _ as *const c_char,
1600 ..Default::default()
1601 };
1602 let kernel = Kernel::from(kernel);
1603 assert_eq!(kernel.kallsyms, MaybeDefault::None);
1604 assert_eq!(kernel.vmlinux, MaybeDefault::None);
1605
1606 let kernel = blaze_symbolize_src_kernel {
1607 kallsyms: b"/proc/kallsyms\0" as *const _ as *const c_char,
1608 vmlinux: b"/boot/vmlinux\0" as *const _ as *const c_char,
1609 debug_syms: false,
1610 ..Default::default()
1611 };
1612
1613 let kernel = Kernel::from(kernel);
1614 assert_eq!(
1615 kernel.kallsyms,
1616 MaybeDefault::Some(PathBuf::from("/proc/kallsyms"))
1617 );
1618 assert_eq!(
1619 kernel.vmlinux,
1620 MaybeDefault::Some(PathBuf::from("/boot/vmlinux"))
1621 );
1622 }
1623
1624 #[tag(miri)]
1627 #[test]
1628 fn cache_process_conversion() {
1629 let process = blaze_cache_src_process {
1630 pid: 42,
1631 cache_vmas: false,
1632 ..Default::default()
1633 };
1634 let process = cache::Process::from(process);
1635 assert_eq!(process.pid, Pid::from(42))
1636 }
1637
1638 #[tag(miri)]
1640 #[test]
1641 fn symbol_conversion() {
1642 fn touch<X: Clone>(x: &X) {
1643 let x = x.clone();
1644 let _x = black_box(x);
1645 }
1646
1647 fn touch_cstr(s: *const c_char) {
1648 if !s.is_null() {
1649 let s = unsafe { CStr::from_ptr(s) }.to_bytes();
1650 let _x = black_box(s);
1651 }
1652 }
1653
1654 fn touch_code_info(code_info: &blaze_symbolize_code_info) {
1655 let blaze_symbolize_code_info {
1656 dir,
1657 file,
1658 line,
1659 column,
1660 reserved: _,
1661 } = code_info;
1662
1663 let _x = touch_cstr(*dir);
1664 let _x = touch_cstr(*file);
1665 let _x = touch(line);
1666 let _x = touch(column);
1667 }
1668
1669 fn touch_syms(syms: *const blaze_syms) {
1671 let syms = unsafe { &*syms };
1672 for i in 0..syms.cnt {
1673 let sym = unsafe { &*syms.syms.as_slice().as_ptr().add(i) };
1674 let blaze_sym {
1675 name,
1676 module,
1677 addr,
1678 offset,
1679 size,
1680 code_info,
1681 inlined_cnt,
1682 inlined,
1683 reason,
1684 reserved: _,
1685 } = sym;
1686
1687 let () = touch_cstr(*name);
1688 let () = touch_cstr(*module);
1689 let _x = touch(addr);
1690 let _x = touch(offset);
1691 let _x = touch(size);
1692 let () = touch_code_info(code_info);
1693
1694 for j in 0..*inlined_cnt {
1695 let inlined_fn = unsafe { &*inlined.add(j) };
1696 let blaze_symbolize_inlined_fn {
1697 name,
1698 code_info,
1699 reserved: _,
1700 } = inlined_fn;
1701 let () = touch_cstr(*name);
1702 let () = touch_code_info(code_info);
1703 }
1704 let () = touch(reason);
1705 }
1706 }
1707
1708 let results = vec![];
1710 let syms = convert_symbolizedresults_to_c(results);
1711 assert!(!syms.is_null());
1712
1713 let () = touch_syms(syms);
1714 let () = unsafe { blaze_syms_free(syms) };
1715
1716 let results = vec![Symbolized::Sym(Sym {
1718 name: "test".into(),
1719 module: Some(Cow::from(OsStr::new("module"))),
1720 addr: 0x1337,
1721 offset: 0x1338,
1722 size: Some(42),
1723 code_info: Some(Box::new(CodeInfo {
1724 dir: None,
1725 file: OsStr::new("a-file").into(),
1726 line: Some(42),
1727 column: Some(43),
1728 _non_exhaustive: (),
1729 })),
1730 inlined: vec![InlinedFn {
1731 name: "inlined_fn".into(),
1732 code_info: Some(CodeInfo {
1733 dir: Some(Path::new("/some/dir").into()),
1734 file: OsStr::new("another-file").into(),
1735 line: Some(42),
1736 column: Some(43),
1737 _non_exhaustive: (),
1738 }),
1739 _non_exhaustive: (),
1740 }]
1741 .into_boxed_slice(),
1742 _non_exhaustive: (),
1743 })];
1744 let syms = convert_symbolizedresults_to_c(results);
1745 assert!(!syms.is_null());
1746 let () = touch_syms(syms);
1747 let () = unsafe { blaze_syms_free(syms) };
1748
1749 let results = vec![
1751 Symbolized::Unknown(Reason::UnknownAddr),
1752 Symbolized::Sym(Sym {
1753 name: "test".into(),
1754 module: Some(Cow::from(OsStr::new("module"))),
1755 addr: 0x1337,
1756 offset: 0x1338,
1757 size: None,
1758 code_info: None,
1759 inlined: vec![InlinedFn {
1760 name: "inlined_fn".into(),
1761 code_info: None,
1762 _non_exhaustive: (),
1763 }]
1764 .into_boxed_slice(),
1765 _non_exhaustive: (),
1766 }),
1767 Symbolized::Unknown(Reason::InvalidFileOffset),
1768 ];
1769 let syms = convert_symbolizedresults_to_c(results);
1770 assert!(!syms.is_null());
1771 let () = touch_syms(syms);
1772 let () = unsafe { blaze_syms_free(syms) };
1773 }
1774
1775 #[tag(miri)]
1777 #[test]
1778 fn symbolizer_creation() {
1779 let symbolizer = blaze_symbolizer_new();
1780 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1781 }
1782
1783 #[tag(miri)]
1786 #[test]
1787 fn symbolizer_creation_with_opts() {
1788 let opts = blaze_symbolizer_opts {
1789 demangle: true,
1790 ..Default::default()
1791 };
1792
1793 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
1794 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1795 }
1796
1797 #[test]
1800 fn symbolize_elf_dwarf_gsym() {
1801 fn test<F>(symbolize: F, has_code_info: bool)
1802 where
1803 F: FnOnce(*mut blaze_symbolizer, *const Addr, usize) -> *const blaze_syms,
1804 {
1805 let symbolizer = blaze_symbolizer_new();
1806 let addrs = [0x2000200];
1807 let result = symbolize(symbolizer, addrs.as_ptr(), addrs.len());
1808
1809 assert!(!result.is_null());
1810
1811 let result = unsafe { &*result };
1812 assert_eq!(result.cnt, 1);
1813 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
1814 let sym = &syms[0];
1815 assert_eq!(
1816 unsafe { CStr::from_ptr(sym.name) },
1817 CStr::from_bytes_with_nul(b"factorial\0").unwrap()
1818 );
1819 assert_eq!(sym.reason, blaze_symbolize_reason::SUCCESS);
1820 assert_eq!(sym.addr, 0x2000200);
1821 assert_eq!(sym.offset, 0);
1822 assert!(sym.size > 0);
1823
1824 if has_code_info {
1825 assert!(!sym.code_info.dir.is_null());
1826 assert!(!sym.code_info.file.is_null());
1827 assert_eq!(
1828 unsafe { CStr::from_ptr(sym.code_info.file) },
1829 CStr::from_bytes_with_nul(b"test-stable-addrs.c\0").unwrap()
1830 );
1831 assert_eq!(sym.code_info.line, 10);
1832 } else {
1833 assert!(sym.code_info.dir.is_null());
1834 assert!(sym.code_info.file.is_null());
1835 assert_eq!(sym.code_info.line, 0);
1836 }
1837
1838 let () = unsafe { blaze_syms_free(result) };
1839 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1840 }
1841
1842 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1843 .join("..")
1844 .join("data")
1845 .join("test-stable-addrs-no-dwarf.bin");
1846 let path_c = CString::new(path.to_str().unwrap()).unwrap();
1847 let elf_src = blaze_symbolize_src_elf {
1848 path: path_c.as_ptr(),
1849 debug_syms: true,
1850 ..Default::default()
1851 };
1852
1853 let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1854 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs, addr_cnt)
1855 };
1856 test(symbolize, false);
1857
1858 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1859 .join("..")
1860 .join("data")
1861 .join("test-stable-addrs-stripped-elf-with-dwarf.bin");
1862 let path_c = CString::new(path.to_str().unwrap()).unwrap();
1863 let elf_src = blaze_symbolize_src_elf {
1864 path: path_c.as_ptr(),
1865 debug_syms: true,
1866 ..Default::default()
1867 };
1868
1869 let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1870 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs, addr_cnt)
1871 };
1872 test(symbolize, true);
1873
1874 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1875 .join("..")
1876 .join("data")
1877 .join("test-stable-addrs.gsym");
1878 let path_c = CString::new(path.to_str().unwrap()).unwrap();
1879 let gsym_src = blaze_symbolize_src_gsym_file {
1880 path: path_c.as_ptr(),
1881 ..Default::default()
1882 };
1883
1884 let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1885 blaze_symbolize_gsym_file_virt_offsets(symbolizer, &gsym_src, addrs, addr_cnt)
1886 };
1887 test(symbolize, true);
1888
1889 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1890 .join("..")
1891 .join("data")
1892 .join("test-stable-addrs.gsym");
1893 let data = read_file(path).unwrap();
1894 let gsym_src = blaze_symbolize_src_gsym_data {
1895 data: data.as_ptr(),
1896 data_len: data.len(),
1897 ..Default::default()
1898 };
1899
1900 let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1901 blaze_symbolize_gsym_data_virt_offsets(symbolizer, &gsym_src, addrs, addr_cnt)
1902 };
1903 test(symbolize, true);
1904 }
1905
1906
1907 #[test]
1909 #[cfg_attr(
1910 not(target_pointer_width = "64"),
1911 ignore = "loads 64 bit shared object"
1912 )]
1913 fn symbolize_elf_file_offset() {
1914 let test_so = Path::new(&env!("CARGO_MANIFEST_DIR"))
1915 .join("..")
1916 .join("data")
1917 .join("libtest-so.so")
1918 .canonicalize()
1919 .unwrap();
1920 let so_cstr = CString::new(test_so.clone().into_os_string().into_vec()).unwrap();
1921 let handle = unsafe { libc::dlopen(so_cstr.as_ptr(), libc::RTLD_NOW) };
1922 assert!(!handle.is_null());
1923
1924 let the_answer_addr = unsafe { libc::dlsym(handle, "the_answer\0".as_ptr().cast()) };
1925 assert!(!the_answer_addr.is_null());
1926
1927 let normalizer = normalize::Normalizer::new();
1928 let normalized = normalizer
1929 .normalize_user_addrs(Pid::Slf, [the_answer_addr as Addr].as_slice())
1930 .unwrap();
1931 assert_eq!(normalized.outputs.len(), 1);
1932 assert_eq!(normalized.meta.len(), 1);
1933
1934 let rc = unsafe { libc::dlclose(handle) };
1935 assert_eq!(rc, 0, "{}", Error::last_os_error());
1936
1937 let output = normalized.outputs[0];
1938 let meta = &normalized.meta[output.1];
1939 assert_eq!(meta.as_elf().unwrap().path, test_so);
1940
1941 let symbolizer = blaze_symbolizer_new();
1942 let elf_src = blaze_symbolize_src_elf {
1943 path: so_cstr.as_ptr(),
1944 ..Default::default()
1945 };
1946 let offsets = [output.0];
1947 let result = unsafe {
1948 blaze_symbolize_elf_file_offsets(
1949 symbolizer,
1950 &elf_src,
1951 offsets.as_slice().as_ptr(),
1952 offsets.len(),
1953 )
1954 };
1955 assert!(!result.is_null());
1956
1957 let result = unsafe { &*result };
1958 assert_eq!(result.cnt, 1);
1959
1960 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
1961 let sym = &syms[0];
1962 assert!(!sym.name.is_null());
1963 assert!(!sym.module.is_null());
1964 assert!(sym.size > 0);
1965 assert_eq!(
1966 unsafe { CStr::from_ptr(sym.name) },
1967 CStr::from_bytes_with_nul(b"the_answer\0").unwrap()
1968 );
1969
1970 let () = unsafe { blaze_syms_free(result) };
1971 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1972 }
1973
1974 #[tag(other_os)]
1977 #[test]
1978 fn symbolize_elf_cached() {
1979 let dir = tempdir().unwrap();
1980 let path__ = Path::new(&env!("CARGO_MANIFEST_DIR"))
1981 .join("..")
1982 .join("data")
1983 .join("test-stable-addrs.bin");
1984 let path = dir.path().join("test-stable-addrs-temporary.bin");
1985 let _count = copy(&path__, &path).unwrap();
1986
1987 let symbolizer = blaze_symbolizer_new();
1988
1989 let path_c = CString::new(path.to_str().unwrap()).unwrap();
1990 let cache = blaze_cache_src_elf {
1991 path: path_c.as_ptr(),
1992 ..Default::default()
1993 };
1994 let () = unsafe { blaze_symbolize_cache_elf(symbolizer, &cache) };
1995 assert_eq!(blaze_err_last(), blaze_err::OK);
1996
1997 let () = remove_file(&path).unwrap();
1998
1999 let src = blaze_symbolize_src_elf {
2000 path: path_c.as_ptr(),
2001 debug_syms: false,
2002 ..Default::default()
2003 };
2004 let addrs = [0x2000200];
2005 let result = unsafe {
2006 blaze_symbolize_elf_virt_offsets(symbolizer, &src, addrs.as_ptr(), addrs.len())
2007 };
2008 assert!(!result.is_null());
2009
2010 let result = unsafe { &*result };
2011 assert_eq!(result.cnt, 1);
2012 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2013 let sym = &syms[0];
2014
2015 assert_eq!(
2016 unsafe { CStr::from_ptr(sym.name) },
2017 CStr::from_bytes_with_nul(b"factorial\0").unwrap()
2018 );
2019 assert_eq!(sym.reason, blaze_symbolize_reason::SUCCESS);
2020 assert_eq!(sym.addr, 0x2000200);
2021
2022 let () = unsafe { blaze_syms_free(result) };
2023 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2024 }
2025
2026 #[test]
2029 fn symbolize_dwarf_demangle() {
2030 fn test(path: &Path, addr: Addr) -> Result<(), ()> {
2031 let opts = blaze_symbolizer_opts {
2032 code_info: true,
2033 inlined_fns: true,
2034 ..Default::default()
2035 };
2036
2037 let path_c = CString::new(path.to_str().unwrap()).unwrap();
2038 let elf_src = blaze_symbolize_src_elf {
2039 path: path_c.as_ptr(),
2040 debug_syms: true,
2041 ..Default::default()
2042 };
2043
2044 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
2045 let addrs = [addr];
2046 let result = unsafe {
2047 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
2048 };
2049 assert!(!result.is_null());
2050
2051 let result = unsafe { &*result };
2052 assert_eq!(result.cnt, 1);
2053 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2054 let sym = &syms[0];
2055 let name = unsafe { CStr::from_ptr(sym.name) };
2056 assert!(
2057 name.to_str().unwrap().contains("test13test_function"),
2058 "{name:?}"
2059 );
2060
2061 if sym.inlined_cnt == 0 {
2062 let () = unsafe { blaze_syms_free(result) };
2063 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2064 return Err(())
2065 }
2066
2067 assert_eq!(sym.inlined_cnt, 1);
2068 let name = unsafe { CStr::from_ptr((*sym.inlined).name) };
2069 assert!(
2070 name.to_str().unwrap().contains("test12inlined_call"),
2071 "{name:?}"
2072 );
2073
2074 let () = unsafe { blaze_syms_free(result) };
2075 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2076
2077 let opts = blaze_symbolizer_opts {
2079 code_info: true,
2080 inlined_fns: true,
2081 demangle: true,
2082 ..Default::default()
2083 };
2084
2085 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
2086 let addrs = [addr];
2087 let result = unsafe {
2088 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
2089 };
2090 assert!(!result.is_null());
2091
2092 let result = unsafe { &*result };
2093 assert_eq!(result.cnt, 1);
2094 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2095 let sym = &syms[0];
2096 assert_eq!(
2097 unsafe { CStr::from_ptr(sym.name) },
2098 CStr::from_bytes_with_nul(b"test::test_function\0").unwrap()
2099 );
2100 assert_eq!(unsafe { CStr::from_ptr(sym.module) }, &*path_c);
2101
2102 assert_eq!(sym.inlined_cnt, 1);
2103 assert_eq!(
2104 unsafe { CStr::from_ptr((*sym.inlined).name) },
2105 CStr::from_bytes_with_nul(b"test::inlined_call\0").unwrap()
2106 );
2107
2108 let () = unsafe { blaze_syms_free(result) };
2109 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2110 Ok(())
2111 }
2112
2113 let test_dwarf = Path::new(&env!("CARGO_MANIFEST_DIR"))
2114 .join("..")
2115 .join("data")
2116 .join("test-rs.bin");
2117 let elf = inspect::source::Elf::new(&test_dwarf);
2118 let src = inspect::source::Source::Elf(elf);
2119
2120 let inspector = inspect::Inspector::new();
2121 let results = inspector
2122 .lookup(&src, &["_RNvCs69hjMPjVIJK_4test13test_function"])
2123 .unwrap()
2124 .into_iter()
2125 .flatten()
2126 .collect::<Vec<_>>();
2127 assert!(!results.is_empty());
2128
2129 let addr = results[0].addr;
2130 let src = Source::Elf(Elf::new(&test_dwarf));
2131 let symbolizer = Symbolizer::builder().enable_demangling(false).build();
2132 let result = symbolizer
2133 .symbolize_single(&src, Input::VirtOffset(addr))
2134 .unwrap()
2135 .into_sym()
2136 .unwrap();
2137
2138 let addr = result.addr;
2139 let size = result.size.unwrap() as u64;
2140 for inst_addr in addr..addr + size {
2141 if test(&test_dwarf, inst_addr).is_ok() {
2142 return
2143 }
2144 }
2145
2146 panic!("failed to find inlined function call");
2147 }
2148
2149 #[test]
2151 fn symbolize_in_process() {
2152 let process_src = blaze_symbolize_src_process {
2153 pid: 0,
2154 debug_syms: true,
2155 perf_map: true,
2156 ..Default::default()
2157 };
2158
2159 let symbolizer = blaze_symbolizer_new();
2160 let addrs = [blaze_symbolizer_new as *const () as Addr];
2161 let result = unsafe {
2162 blaze_symbolize_process_abs_addrs(symbolizer, &process_src, addrs.as_ptr(), addrs.len())
2163 };
2164
2165 assert!(!result.is_null());
2166
2167 let result = unsafe { &*result };
2168 assert_eq!(result.cnt, 1);
2169 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2170 let sym = &syms[0];
2171 assert_eq!(
2172 unsafe { CStr::from_ptr(sym.name) },
2173 CStr::from_bytes_with_nul(b"blaze_symbolizer_new\0").unwrap()
2174 );
2175
2176 let () = unsafe { blaze_syms_free(result) };
2177 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2178 }
2179
2180 #[test]
2183 fn symbolize_in_process_cached() {
2184 let symbolizer = blaze_symbolizer_new();
2185 let cache = blaze_cache_src_process {
2186 pid: 0,
2187 cache_vmas: true,
2188 ..Default::default()
2189 };
2190 let () = unsafe { blaze_symbolize_cache_process(symbolizer, &cache) };
2191 assert_eq!(blaze_err_last(), blaze_err::OK);
2192
2193 let src = blaze_symbolize_src_process {
2194 pid: 0,
2195 debug_syms: true,
2196 perf_map: true,
2197 ..Default::default()
2198 };
2199 let addrs = [blaze_symbolizer_new as *const () as Addr];
2200 let result = unsafe {
2201 blaze_symbolize_process_abs_addrs(symbolizer, &src, addrs.as_ptr(), addrs.len())
2202 };
2203
2204 assert!(!result.is_null());
2205
2206 let result = unsafe { &*result };
2207 assert_eq!(result.cnt, 1);
2208 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2209 let sym = &syms[0];
2210 assert_eq!(
2211 unsafe { CStr::from_ptr(sym.name) },
2212 CStr::from_bytes_with_nul(b"blaze_symbolizer_new\0").unwrap()
2213 );
2214
2215 let () = unsafe { blaze_syms_free(result) };
2216 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2217 }
2218
2219 #[test]
2222 fn symbolize_in_kernel() {
2223 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2224 .join("..")
2225 .join("data")
2226 .join("kallsyms");
2227 let path_c = CString::new(path.to_str().unwrap()).unwrap();
2228 let src = blaze_symbolize_src_kernel {
2229 kallsyms: path_c.as_ptr(),
2230 vmlinux: b"\0" as *const _ as *const c_char,
2231 ..Default::default()
2232 };
2233
2234 let symbolizer = blaze_symbolizer_new();
2235 let addrs = [0xc080a470];
2236 let result = unsafe {
2237 blaze_symbolize_kernel_abs_addrs(symbolizer, &src, addrs.as_ptr(), addrs.len())
2238 };
2239
2240 assert!(!result.is_null());
2241
2242 let result = unsafe { &*result };
2243 assert_eq!(result.cnt, 1);
2244 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2245 let sym = &syms[0];
2246 assert_eq!(
2247 unsafe { CStr::from_ptr(sym.name) },
2248 CStr::from_bytes_with_nul(b"init_task\0").unwrap()
2249 );
2250 assert_eq!(sym.module, ptr::null_mut());
2251
2252 let () = unsafe { blaze_syms_free(result) };
2253 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2254 }
2255
2256 #[test]
2259 fn symbolize_elf_non_existent() {
2260 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2261 .join("..")
2262 .join("data")
2263 .join("does-not-actually-exist.bin");
2264 let path_c = CString::new(path.to_str().unwrap()).unwrap();
2265 let elf_src = blaze_symbolize_src_elf {
2266 path: path_c.as_ptr(),
2267 debug_syms: true,
2268 ..Default::default()
2269 };
2270
2271 let symbolizer = blaze_symbolizer_new();
2272 let addrs = [blaze_symbolizer_new as *const () as Addr];
2273 let result = unsafe {
2274 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
2275 };
2276 assert!(result.is_null());
2277 assert_eq!(blaze_err_last(), blaze_err::NOT_FOUND);
2278
2279 let () = unsafe { blaze_syms_free(result) };
2280 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2281 }
2282
2283 #[test]
2285 fn symbolize_no_debug_dirs() {
2286 let dir = tempdir().unwrap();
2287 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2288 .join("..")
2289 .join("data")
2290 .join("test-stable-addrs-stripped-with-link.bin");
2291 let dst = dir.path().join("test-stable-addrs-stripped-with-link.bin");
2292 let _count = copy(path, &dst).unwrap();
2293
2294 let debug_dirs = [];
2295 let opts = blaze_symbolizer_opts {
2296 debug_dirs: debug_dirs.as_ptr(),
2297 debug_dirs_len: debug_dirs.len(),
2298 code_info: true,
2299 inlined_fns: true,
2300 demangle: true,
2301 ..Default::default()
2302 };
2303 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
2304
2305 let path_c = CString::new(dst.to_str().unwrap()).unwrap();
2306 let elf_src = blaze_symbolize_src_elf {
2307 path: path_c.as_ptr(),
2308 debug_syms: true,
2309 ..Default::default()
2310 };
2311 let addrs = [0x2000200];
2312 let result = unsafe {
2313 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
2314 };
2315 assert!(!result.is_null());
2316
2317 let result = unsafe { &*result };
2318 assert_eq!(result.cnt, 1);
2319 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2320 let sym = &syms[0];
2321 assert_eq!(sym.name, ptr::null());
2324 assert_eq!(sym.module, ptr::null());
2325 assert_eq!(sym.reason, blaze_symbolize_reason::MISSING_SYMS);
2326
2327 let () = unsafe { blaze_syms_free(result) };
2328 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2329 }
2330
2331 #[test]
2333 fn symbolize_configurable_debug_dirs() {
2334 let debug_dir1 = tempdir().unwrap();
2335 let debug_dir1_c = CString::new(debug_dir1.path().to_str().unwrap()).unwrap();
2336 let debug_dir2 = tempdir().unwrap();
2337 let debug_dir2_c = CString::new(debug_dir2.path().to_str().unwrap()).unwrap();
2338
2339 let src = Path::new(&env!("CARGO_MANIFEST_DIR"))
2340 .join("..")
2341 .join("data")
2342 .join("test-stable-addrs-dwarf-only.dbg");
2343 let dst = debug_dir2.path().join("test-stable-addrs-dwarf-only.dbg");
2344 let _count = copy(src, dst).unwrap();
2345
2346 let debug_dirs = [debug_dir1_c.as_ptr(), debug_dir2_c.as_ptr()];
2347 let opts = blaze_symbolizer_opts {
2348 debug_dirs: debug_dirs.as_ptr(),
2349 debug_dirs_len: debug_dirs.len(),
2350 code_info: true,
2351 inlined_fns: true,
2352 demangle: true,
2353 ..Default::default()
2354 };
2355 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
2356
2357 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2358 .join("..")
2359 .join("data")
2360 .join("test-stable-addrs-stripped-with-link.bin");
2361 let path_c = CString::new(path.to_str().unwrap()).unwrap();
2362 let elf_src = blaze_symbolize_src_elf {
2363 path: path_c.as_ptr(),
2364 debug_syms: true,
2365 ..Default::default()
2366 };
2367 let addrs = [0x2000200];
2368 let result = unsafe {
2369 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
2370 };
2371 assert!(!result.is_null());
2372
2373 let result = unsafe { &*result };
2374 assert_eq!(result.cnt, 1);
2375 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2376 let sym = &syms[0];
2377 let name = unsafe { CStr::from_ptr(sym.name) };
2378 assert_eq!(name.to_str().unwrap(), "factorial");
2379 let module = unsafe { CStr::from_ptr(sym.module) };
2380 assert!(
2381 module
2382 .to_str()
2383 .unwrap()
2384 .ends_with("test-stable-addrs-stripped-with-link.bin"),
2385 "{module:?}"
2386 );
2387
2388 let () = unsafe { blaze_syms_free(result) };
2389 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2390 }
2391
2392 fn symbolize_with_dispatch(
2395 cb: unsafe extern "C" fn(*const c_char, *const c_char, *mut c_void) -> *mut c_char,
2396 addr: Addr,
2397 expected_sym: Option<&str>,
2398 ) {
2399 let dispatch = blaze_symbolizer_dispatch {
2400 dispatch_cb: cb,
2401 ctx: 0xc0ffee as *mut c_void,
2402 };
2403 let opts = blaze_symbolizer_opts {
2404 process_dispatch: &dispatch,
2405 ..Default::default()
2406 };
2407 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
2408 assert!(!symbolizer.is_null());
2409
2410 let process_src = blaze_symbolize_src_process {
2411 pid: 0,
2412 debug_syms: true,
2413 ..Default::default()
2414 };
2415
2416 let addrs = [addr];
2417 let result = unsafe {
2418 blaze_symbolize_process_abs_addrs(symbolizer, &process_src, addrs.as_ptr(), addrs.len())
2419 };
2420 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2421
2422 if let Some(expected) = expected_sym {
2423 assert!(!result.is_null());
2424 let result = unsafe { &*result };
2425 assert_eq!(result.cnt, 1);
2426 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2427 let name = unsafe { CStr::from_ptr(syms[0].name) }.to_str().unwrap();
2428 assert!(name.contains(expected), "{name}");
2429 let () = unsafe { blaze_syms_free(result) };
2430 } else {
2431 assert!(result.is_null());
2432 }
2433 }
2434
2435 #[test]
2439 fn symbolize_in_process_with_dispatch() {
2440 unsafe extern "C" fn cb(
2441 maps_file: *const c_char,
2442 _symbolic_path: *const c_char,
2443 ctx: *mut c_void,
2444 ) -> *mut c_char {
2445 assert_eq!(ctx as usize, 0xc0ffee);
2446 unsafe { libc::strdup(maps_file) }
2447 }
2448
2449 symbolize_with_dispatch(
2450 cb,
2451 symbolize_in_process_with_dispatch as *const () as Addr,
2452 Some("symbolize_in_process_with_dispatch"),
2453 )
2454 }
2455
2456 #[test]
2459 fn symbolize_in_process_with_null_dispatch() {
2460 unsafe extern "C" fn cb(
2461 _maps_file: *const c_char,
2462 _symbolic_path: *const c_char,
2463 ctx: *mut c_void,
2464 ) -> *mut c_char {
2465 assert_eq!(ctx as usize, 0xc0ffee);
2466 ptr::null_mut()
2467 }
2468
2469 symbolize_with_dispatch(
2470 cb,
2471 symbolize_in_process_with_null_dispatch as *const () as Addr,
2472 Some("symbolize_in_process_with_null_dispatch"),
2473 )
2474 }
2475
2476 #[test]
2479 fn symbolize_in_process_with_bad_dispatch() {
2480 unsafe extern "C" fn cb(
2481 _maps_file: *const c_char,
2482 _symbolic_path: *const c_char,
2483 ctx: *mut c_void,
2484 ) -> *mut c_char {
2485 assert_eq!(ctx as usize, 0xc0ffee);
2486 unsafe { libc::strdup(c"/no/such/file".as_ptr()) }
2487 }
2488
2489 symbolize_with_dispatch(
2490 cb,
2491 symbolize_in_process_with_bad_dispatch as *const () as Addr,
2492 None,
2493 )
2494 }
2495}