|
1 | | -use std::{ |
2 | | - cell::UnsafeCell, |
3 | | - ffi::CStr, |
4 | | - marker::PhantomData, |
5 | | - ops::Deref, |
6 | | - sync::atomic::{AtomicBool, Ordering}, |
7 | | -}; |
8 | | - |
9 | | -use crate::client::log::error; |
10 | | - |
11 | 1 | pub mod sidecar_ffi; |
12 | 2 |
|
13 | | -#[macro_export] |
14 | | -macro_rules! sidecar_symbol { |
15 | | - // form 1: inline function signature |
16 | | - ( |
17 | | - static $static:ident = |
18 | | - fn($($arg:ty),* $(, ...)? $(,)?) $(-> $ret:ty)? : |
19 | | - $name:ident |
20 | | - ) => { |
21 | | - type $name = unsafe extern "C" fn( |
22 | | - $($arg),* |
23 | | - $(, ...)? |
24 | | - ) $(-> $ret)?; |
25 | | - |
26 | | - static $static: SidecarSymbol<$name> = |
27 | | - SidecarSymbol::new(::std::concat!(::std::stringify!($name), "\0")); |
28 | | - |
29 | | - const _: () = { |
30 | | - let _: $name = $name; |
31 | | - }; |
32 | | - }; |
33 | | - |
34 | | - // form 2: type alias |
35 | | - ( |
36 | | - static $static:ident = |
37 | | - $ty:ty : |
38 | | - $name:ident |
39 | | - ) => { |
40 | | - static $static: SidecarSymbol<$ty> = |
41 | | - SidecarSymbol::new(unsafe {::std::ffi::CStr::from_bytes_with_nul_unchecked(::std::concat!(::std::stringify!($name), "\0").as_bytes())}); |
42 | | - |
43 | | - const _: () = { |
44 | | - let _: $ty = $name; |
45 | | - }; |
46 | | - }; |
47 | | -} |
48 | | - |
49 | | -pub struct SidecarSymbol<Func> { |
50 | | - // In order to implement Deref, we need to have a sort of rvalue for the function |
51 | | - // So do not use an AtomicPtr that we check for null to determine if we're initialized |
52 | | - func_ptr: UnsafeCell<*mut libc::c_void>, |
53 | | - initialized: AtomicBool, |
54 | | - name: &'static CStr, |
55 | | - _phantom: PhantomData<Func>, |
56 | | -} |
57 | | -impl<Func> SidecarSymbol<Func> { |
58 | | - pub const fn new(name: &'static CStr) -> Self { |
59 | | - assert!( |
60 | | - std::mem::size_of::<Func>() == std::mem::size_of::<*mut libc::c_void>(), |
61 | | - "Func must be pointer-sized" |
62 | | - ); |
63 | | - Self { |
64 | | - func_ptr: UnsafeCell::new(std::ptr::null_mut()), |
65 | | - initialized: AtomicBool::new(false), |
66 | | - name, |
67 | | - _phantom: PhantomData, |
68 | | - } |
| 3 | +// Stub implementations of the sidecar symbols for `cargo test`. |
| 4 | +// In production the real symbols are provided by datadog-ipc-helper at |
| 5 | +// dlopen time. The cdylib has no stubs (--allow-shlib-undefined covers it); |
| 6 | +// the test executable needs concrete definitions at link time because lld on |
| 7 | +// musl does not allow undefined symbols in executables. |
| 8 | +#[cfg(test)] |
| 9 | +mod test_stubs { |
| 10 | + use super::sidecar_ffi::*; |
| 11 | + |
| 12 | + #[no_mangle] |
| 13 | + extern "C" fn ddog_Error_drop(_: *mut ddog_Error) {} |
| 14 | + #[no_mangle] |
| 15 | + extern "C" fn ddog_Error_message(_: *const ddog_Error) -> ddog_CharSlice { |
| 16 | + ddog_CharSlice { ptr: std::ptr::null(), len: 0 } |
69 | 17 | } |
70 | | - |
71 | | - pub fn resolve(&self) -> anyhow::Result<()> { |
72 | | - if self.initialized.load(Ordering::Acquire) { |
73 | | - return Ok(()); |
74 | | - } |
75 | | - |
76 | | - let func_ptr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, self.name.as_ptr()) }; |
77 | | - if func_ptr.is_null() { |
78 | | - return Err(anyhow::anyhow!("Failed to resolve symbol: {:?}", self.name)); |
79 | | - } |
80 | | - unsafe { std::ptr::write(self.func_ptr.get(), func_ptr) }; |
81 | | - self.initialized.store(true, Ordering::Release); |
82 | | - Ok(()) |
| 18 | + #[no_mangle] |
| 19 | + extern "C" fn ddog_MaybeError_drop(_: ddog_MaybeError) {} |
| 20 | + #[no_mangle] |
| 21 | + extern "C" fn ddog_set_rc_notify_fn(_: ddog_InProcNotifyFn) {} |
| 22 | + #[no_mangle] |
| 23 | + unsafe extern "C" fn ddog_remote_config_path( |
| 24 | + _: *const ddog_ConfigInvariants, |
| 25 | + _: *const ddog_Arc_Target, |
| 26 | + ) -> *mut std::ffi::c_char { |
| 27 | + std::ptr::null_mut() |
83 | 28 | } |
84 | | - |
85 | | - fn get(&self) -> Option<&Func> { |
86 | | - if !self.initialized.load(Ordering::Acquire) { |
87 | | - None |
88 | | - } else { |
89 | | - Some(unsafe { &*self.func_ptr.get().cast() }) |
| 29 | + #[no_mangle] |
| 30 | + extern "C" fn ddog_remote_config_path_free(_: *mut std::ffi::c_char) {} |
| 31 | + #[no_mangle] |
| 32 | + unsafe extern "C" fn ddog_sidecar_connect( |
| 33 | + _: *mut *mut ddog_SidecarTransport, |
| 34 | + ) -> ddog_MaybeError { |
| 35 | + ddog_MaybeError { |
| 36 | + tag: ddog_Option_Error_Tag_DDOG_OPTION_ERROR_NONE_ERROR, |
| 37 | + __bindgen_anon_1: unsafe { std::mem::zeroed() }, |
90 | 38 | } |
91 | 39 | } |
92 | | -} |
93 | | -unsafe impl<Func> Sync for SidecarSymbol<Func> {} |
94 | | -impl<Func> Deref for SidecarSymbol<Func> { |
95 | | - type Target = Func; |
96 | | - |
97 | | - fn deref(&self) -> &Self::Target { |
98 | | - match self.get() { |
99 | | - Some(func) => func, |
100 | | - None => { |
101 | | - error!("Symbol is not resolved, will panic: {:?}", self); |
102 | | - panic!("Symbol is not resolved: {:?}", self.name); |
103 | | - } |
| 40 | + #[no_mangle] |
| 41 | + extern "C" fn ddog_sidecar_transport_drop(_: *mut ddog_SidecarTransport) {} |
| 42 | + #[no_mangle] |
| 43 | + unsafe extern "C" fn ddog_sidecar_ping( |
| 44 | + _: *mut *mut ddog_SidecarTransport, |
| 45 | + ) -> ddog_MaybeError { |
| 46 | + ddog_MaybeError { |
| 47 | + tag: ddog_Option_Error_Tag_DDOG_OPTION_ERROR_NONE_ERROR, |
| 48 | + __bindgen_anon_1: unsafe { std::mem::zeroed() }, |
104 | 49 | } |
105 | 50 | } |
106 | | -} |
107 | | -impl<Func> std::fmt::Debug for SidecarSymbol<Func> { |
108 | | - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
109 | | - let initialized = self.initialized.load(Ordering::Acquire); |
110 | | - let mut ds = f.debug_struct("SidecarSymbol"); |
111 | | - ds.field("name", &self.name) |
112 | | - .field("name", &self.name) |
113 | | - .field("initialized", &initialized); |
114 | | - if initialized { |
115 | | - ds.field("func_ptr", &self.func_ptr.get()); |
| 51 | + #[no_mangle] |
| 52 | + unsafe extern "C" fn ddog_sidecar_enqueue_telemetry_log( |
| 53 | + _: ddog_CharSlice, _: ddog_CharSlice, _: ddog_CharSlice, _: ddog_CharSlice, |
| 54 | + _: ddog_CharSlice, _: ddog_LogLevel, _: ddog_CharSlice, |
| 55 | + _: *mut ddog_CharSlice, _: *mut ddog_CharSlice, _: bool, |
| 56 | + ) -> ddog_MaybeError { |
| 57 | + ddog_MaybeError { |
| 58 | + tag: ddog_Option_Error_Tag_DDOG_OPTION_ERROR_NONE_ERROR, |
| 59 | + __bindgen_anon_1: unsafe { std::mem::zeroed() }, |
116 | 60 | } |
117 | | - ds.finish() |
118 | 61 | } |
119 | | -} |
120 | | - |
121 | | -#[cfg(test)] |
122 | | -mod tests { |
123 | | - use super::*; |
124 | | - use std::ffi::CStr; |
125 | | - use std::path::PathBuf; |
126 | | - |
127 | | - fn load_library(path: &std::path::Path) -> *mut libc::c_void { |
128 | | - let path_cstr = std::ffi::CString::new(path.to_str().unwrap()).expect("Invalid path"); |
129 | | - |
130 | | - let handle = |
131 | | - unsafe { libc::dlopen(path_cstr.as_ptr(), libc::RTLD_NOW | libc::RTLD_GLOBAL) }; |
132 | | - |
133 | | - if handle.is_null() { |
134 | | - let error = unsafe { libc::dlerror() }; |
135 | | - if !error.is_null() { |
136 | | - let error_str = unsafe { CStr::from_ptr(error) }; |
137 | | - panic!("dlopen failed: {:?}", error_str); |
138 | | - } |
139 | | - panic!("dlopen failed with unknown error"); |
| 62 | + #[no_mangle] |
| 63 | + unsafe extern "C" fn ddog_sidecar_enqueue_telemetry_point( |
| 64 | + _: ddog_CharSlice, _: ddog_CharSlice, _: ddog_CharSlice, _: ddog_CharSlice, |
| 65 | + _: ddog_CharSlice, _: f64, _: *mut ddog_CharSlice, |
| 66 | + ) -> ddog_MaybeError { |
| 67 | + ddog_MaybeError { |
| 68 | + tag: ddog_Option_Error_Tag_DDOG_OPTION_ERROR_NONE_ERROR, |
| 69 | + __bindgen_anon_1: unsafe { std::mem::zeroed() }, |
140 | 70 | } |
141 | | - |
142 | | - handle |
143 | 71 | } |
144 | | - |
145 | | - fn unload_library(handle: *mut libc::c_void) { |
146 | | - if !handle.is_null() { |
147 | | - unsafe { |
148 | | - libc::dlclose(handle); |
149 | | - } |
| 72 | + #[no_mangle] |
| 73 | + unsafe extern "C" fn ddog_sidecar_enqueue_telemetry_metric( |
| 74 | + _: ddog_CharSlice, _: ddog_CharSlice, _: ddog_CharSlice, _: ddog_CharSlice, |
| 75 | + _: ddog_CharSlice, _: ddog_MetricType, _: ddog_MetricNamespace, |
| 76 | + ) -> ddog_MaybeError { |
| 77 | + ddog_MaybeError { |
| 78 | + tag: ddog_Option_Error_Tag_DDOG_OPTION_ERROR_NONE_ERROR, |
| 79 | + __bindgen_anon_1: unsafe { std::mem::zeroed() }, |
150 | 80 | } |
151 | 81 | } |
152 | | - |
153 | | - type TestAddFn = unsafe extern "C" fn(i32, i32) -> i32; |
154 | | - |
155 | | - #[test] |
156 | | - fn test_sidecar_symbol_resolve_and_call() { |
157 | | - #[cfg(target_os = "macos")] |
158 | | - let lib_name = "libtest_sidecar.dylib"; |
159 | | - #[cfg(target_os = "linux")] |
160 | | - let lib_name = "libtest_sidecar.so"; |
161 | | - let lib_path = PathBuf::from(env!("OUT_DIR")).join(lib_name); |
162 | | - let handle = load_library(&lib_path); |
163 | | - |
164 | | - static TEST_ADD_SYMBOL: SidecarSymbol<TestAddFn> = SidecarSymbol::new(c"test_add"); |
165 | | - TEST_ADD_SYMBOL |
166 | | - .resolve() |
167 | | - .expect("Failed to resolve test_add symbol"); |
168 | | - |
169 | | - let result = unsafe { TEST_ADD_SYMBOL(3, 4) }; |
170 | | - assert_eq!(result, 7, "test_add(3, 4) should return 7"); |
171 | | - |
172 | | - unload_library(handle); |
173 | | - } |
174 | | - |
175 | | - #[test] |
176 | | - fn test_sidecar_symbol_unresolved_symbol_fails() { |
177 | | - static NONEXISTENT_SYMBOL: SidecarSymbol<TestAddFn> = |
178 | | - SidecarSymbol::new(c"nonexistent_function_12345"); |
179 | | - |
180 | | - let result = NONEXISTENT_SYMBOL.resolve(); |
181 | | - assert!(result.is_err(), "Resolving nonexistent symbol should fail"); |
182 | | - } |
183 | | - |
184 | | - #[test] |
185 | | - fn test_sidecar_symbol_debug_format() { |
186 | | - static DEBUG_TEST_SYMBOL: SidecarSymbol<TestAddFn> = SidecarSymbol::new(c"debug_test_func"); |
187 | | - |
188 | | - let debug_str = format!("{:?}", DEBUG_TEST_SYMBOL); |
189 | | - assert!( |
190 | | - debug_str.contains("initialized: false"), |
191 | | - "Unresolved symbol should show initialized: false" |
192 | | - ); |
193 | | - assert!( |
194 | | - debug_str.contains("debug_test_func"), |
195 | | - "Debug output should contain the symbol name" |
196 | | - ); |
197 | | - } |
198 | 82 | } |
| 83 | + |
0 commit comments