1#![allow(unused_variables)]
6
7use std::ffi::CStr;
8
9use libc::{c_char, c_int, size_t};
10
11pub const TRACE_ERROR: c_int = 1;
12pub const TRACE_INFO: c_int = 2;
13pub const TRACE_DEBUG: c_int = 3;
14pub const TRACE_FLOW: c_int = 4;
15
16fn get_thread_id() -> i32 {
17 unsafe { libc::syscall(libc::SYS_gettid) as i32 }
18}
19
20#[allow(dead_code)]
22fn is_printable_ascii(byte: u8) -> bool {
23 (32..=126).contains(&byte)
24}
25
26fn get_level_string(level: c_int) -> &'static str {
28 match level {
29 TRACE_ERROR => "ERR",
30 TRACE_INFO => "INF",
31 TRACE_DEBUG => "DBG",
32 TRACE_FLOW => "FLW",
33 _ => "",
34 }
35}
36
37#[expect(clippy::not_unsafe_ptr_arg_deref)]
53#[unsafe(no_mangle)]
54pub extern "C" fn rust_dprintf(
55 function: *const c_char,
56 line: c_int,
57 level: c_int,
58 prefix: *const c_char,
59 message: *const c_char,
60) -> c_int {
61 if prefix.is_null() || message.is_null() {
62 return -1;
63 }
64
65 let msg = {
66 let c_str = unsafe { CStr::from_ptr(message) };
68 match c_str.to_str() {
69 Ok(s) => s,
70 Err(_) => return -1,
71 }
72 };
73
74 let output = if !function.is_null() && line > 0 {
75 let function_str = unsafe { CStr::from_ptr(function) }
78 .to_str()
79 .unwrap_or("<invalid_utf8>");
80
81 let prefix_str = unsafe { CStr::from_ptr(prefix) }
83 .to_str()
84 .unwrap_or("<invalid_utf8>");
85 let thread_id = get_thread_id();
86 let level_str = get_level_string(level);
87
88 format!("{level_str} [{thread_id}] {prefix_str}:{function_str}:{line}: {msg}\n")
89 } else {
90 format!("{msg}\n")
91 };
92
93 print!("{output}");
94
95 output.len() as c_int
96}
97
98#[cfg(any(feature = "debug_level_3", feature = "debug_level_4"))]
108#[expect(clippy::not_unsafe_ptr_arg_deref)]
109#[unsafe(no_mangle)]
110pub extern "C" fn dump_buffer(bname: *const c_char, buffer: *const u8, blen: size_t) {
111 if bname.is_null() || buffer.is_null() || blen == 0 {
112 return;
113 }
114
115 let bname_str = unsafe { CStr::from_ptr(bname) }
118 .to_str()
119 .unwrap_or("<invalid_utf8>");
120
121 eprintln!("#### {bname_str}");
122
123 let buffer_slice = unsafe { std::slice::from_raw_parts(buffer, blen) };
125 let mut offset = 0;
126
127 while offset < buffer_slice.len() {
128 for i in 0..16 {
130 if offset + i < buffer_slice.len() {
131 eprint!("{:02x} ", buffer_slice[offset + i]);
132 } else {
133 eprint!(" "); }
135
136 if i == 7 {
137 eprint!(" "); }
139 }
140
141 eprint!(" |");
142
143 for i in 0..16 {
145 if offset + i < buffer_slice.len() {
146 let byte = buffer_slice[offset + i];
147 if is_printable_ascii(byte) {
148 eprint!("{}", byte as char);
149 } else {
150 eprint!("."); }
152 }
153 }
154
155 eprintln!("|");
156
157 offset += 16;
158 }
159}
160
161#[cfg(not(all(any(feature = "debug_level_3", feature = "debug_level_4"))))]
165#[unsafe(no_mangle)]
166pub extern "C" fn dump_buffer(bname: *const c_char, buffer: *const u8, blen: size_t) {}
167
168#[cfg(test)]
169mod teec_trace_tests {
170 use super::*;
171 use std::ffi::CString;
172
173 fn make_c_string_with_ptr(s: &str) -> (CString, *const c_char) {
174 let cstr = CString::new(s).expect("Failed to create CString");
175 let ptr = cstr.as_ptr();
176 (cstr, ptr)
177 }
178
179 #[test]
180 fn test_is_printable_ascii() {
181 assert!(is_printable_ascii(b'A'));
183 assert!(is_printable_ascii(b'z'));
184 assert!(is_printable_ascii(b' '));
185 assert!(is_printable_ascii(b'~'));
186
187 assert!(!is_printable_ascii(0));
188 assert!(!is_printable_ascii(31));
189 assert!(!is_printable_ascii(127));
190 assert!(!is_printable_ascii(128));
191 assert!(!is_printable_ascii(255));
192 }
193
194 #[test]
195 fn test_get_level_string() {
196 assert_eq!(get_level_string(TRACE_ERROR), "ERR");
198 assert_eq!(get_level_string(TRACE_INFO), "INF");
199 assert_eq!(get_level_string(TRACE_DEBUG), "DBG");
200 assert_eq!(get_level_string(TRACE_FLOW), "FLW");
201 assert_eq!(get_level_string(0), ""); assert_eq!(get_level_string(999), ""); }
204
205 #[test]
206 fn test_rust_dprintf_valid_input() {
207 let function = Some("test_function");
208 let line = 42;
209 let level = TRACE_INFO;
210 let prefix = "mylib";
211 let message = "This is a test message";
212
213 let (function_cstr, function_ptr) = if let Some(func) = function {
214 make_c_string_with_ptr(func)
215 } else {
216 let cstr = CString::new("").expect("Empty string should always create CString");
218 (cstr, std::ptr::null())
219 };
220
221 let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr(prefix);
222 let (message_cstr, message_ptr) = make_c_string_with_ptr(message);
223
224 let result = rust_dprintf(function_ptr, line, level, prefix_ptr, message_ptr);
225
226 assert!(result > 0);
227 }
228
229 #[test]
230 fn test_rust_dprintf_different_levels() {
231 for (level, _expected_prefix) in [
232 (TRACE_ERROR, "ERR"),
233 (TRACE_INFO, "INF"),
234 (TRACE_DEBUG, "DBG"),
235 (TRACE_FLOW, "FLW"),
236 ] {
237 let function = Some("func");
238 let line = 1;
239 let prefix = "prefix";
240 let message = "message";
241
242 let (function_cstr, function_ptr) = if let Some(func) = function {
243 make_c_string_with_ptr(func)
244 } else {
245 let cstr = CString::new("").expect("Empty string should always create CString");
247 (cstr, std::ptr::null())
248 };
249
250 let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr(prefix);
251 let (message_cstr, message_ptr) = make_c_string_with_ptr(message);
252
253 let result = rust_dprintf(function_ptr, line, level, prefix_ptr, message_ptr);
254
255 assert!(result > 0);
256 }
257 }
258
259 #[test]
260 fn test_rust_dprintf_no_function_info() {
261 let level = TRACE_INFO;
262 let prefix = "prefix";
263 let message = "simple message";
264
265 let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr(prefix);
266 let (message_cstr, message_ptr) = make_c_string_with_ptr(message);
267
268 let result = rust_dprintf(std::ptr::null(), 0, level, prefix_ptr, message_ptr);
269
270 assert_eq!(result, (message.len() + 1) as i32); }
274
275 #[test]
276 fn test_rust_dprintf_null_pointers() {
277 let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr("prefix");
279 let result = rust_dprintf(
280 std::ptr::null(),
281 0,
282 TRACE_INFO,
283 prefix_ptr,
284 std::ptr::null(),
285 );
286 assert_eq!(result, -1);
287
288 let (message_cstr, message_ptr) = make_c_string_with_ptr("message");
289 let result = rust_dprintf(
290 std::ptr::null(),
291 0,
292 TRACE_INFO,
293 std::ptr::null(),
294 message_ptr,
295 );
296 assert_eq!(result, -1);
297
298 let result = rust_dprintf(
299 std::ptr::null(),
300 0,
301 TRACE_INFO,
302 std::ptr::null(),
303 std::ptr::null(),
304 );
305 assert_eq!(result, -1);
306 }
307
308 #[test]
309 fn test_rust_dprintf_invalid_utf8() {
310 let invalid_utf8 = b"valid\xFFpart\0";
312 let cstr = unsafe { CStr::from_bytes_with_nul_unchecked(invalid_utf8) };
313 let ptr = cstr.as_ptr();
314
315 let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr("prefix");
316 let result = rust_dprintf(std::ptr::null(), 0, TRACE_INFO, prefix_ptr, ptr);
317
318 assert_eq!(result, -1);
320 }
321
322 #[test]
323 fn test_thread_id_positive() {
324 let thread_id = get_thread_id();
326 assert!(thread_id > 0);
327 }
328
329 #[test]
330 fn test_integration_style() {
331 let test_cases = vec![
333 (
334 Some("connect"),
335 101,
336 TRACE_ERROR,
337 "network",
338 "Connection failed",
339 ),
340 (
341 Some("process"),
342 205,
343 TRACE_INFO,
344 "worker",
345 "Processing item",
346 ),
347 (
348 Some("validate"),
349 42,
350 TRACE_DEBUG,
351 "parser",
352 "Validating input",
353 ),
354 (Some("loop"), 15, TRACE_FLOW, "main", "Iteration complete"),
355 ];
356
357 for (func, line, level, prefix, msg) in test_cases {
358 let (function_cstr, function_ptr) = if let Some(f) = func {
359 make_c_string_with_ptr(f)
360 } else {
361 let cstr = CString::new("").expect("Empty string should always create CString");
363 (cstr, std::ptr::null())
364 };
365
366 let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr(prefix);
367 let (message_cstr, message_ptr) = make_c_string_with_ptr(msg);
368
369 let result = rust_dprintf(function_ptr, line, level, prefix_ptr, message_ptr);
370
371 assert!(
373 result > 0,
374 "Failed for case: {:?}",
375 (func, line, level, prefix, msg)
376 );
377 }
378 }
379
380 #[test]
381 fn test_performance_no_panics() {
382 let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr("perf");
384 let (message_cstr, message_ptr) = make_c_string_with_ptr("test");
385
386 for _ in 0..1000 {
387 let result = rust_dprintf(std::ptr::null(), 0, TRACE_INFO, prefix_ptr, message_ptr);
388 assert!(result >= 0);
389 }
390 }
391
392 #[test]
394 fn test_dump_buffer_exists() {
395 let buffer = b"Hello, World!";
397 let (name_cstr, name_ptr) = make_c_string_with_ptr("test_buffer");
398
399 dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
401
402 dump_buffer(std::ptr::null(), std::ptr::null(), 0);
404 dump_buffer(name_ptr, std::ptr::null(), 10);
405 dump_buffer(std::ptr::null(), buffer.as_ptr(), buffer.len());
406 }
407
408 #[test]
409 fn test_dump_buffer_edge_cases() {
410 let buffer = vec![0u8; 0];
412 let (name_cstr, name_ptr) = make_c_string_with_ptr("empty");
413
414 dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
415
416 let small_buffer = b"short";
417 let (name2_cstr, name2_ptr) = make_c_string_with_ptr("small");
418
419 dump_buffer(name2_ptr, small_buffer.as_ptr(), small_buffer.len());
420
421 let non_printable = vec![0u8, 1, 2, 3, 128, 255, 10, 13];
422 let (name3_cstr, name3_ptr) = make_c_string_with_ptr("non_printable");
423
424 dump_buffer(name3_ptr, non_printable.as_ptr(), non_printable.len());
425 }
426}
427
428#[cfg(all(test, any(feature = "debug_level_3", feature = "debug_level_4")))]
429mod dump_buffer_tests {
430 use super::*;
431 use std::ffi::CString;
432
433 fn make_c_string_with_ptr(s: &str) -> (CString, *const c_char) {
435 let cstr = CString::new(s).expect("Failed to create CString");
436 let ptr = cstr.as_ptr();
437 (cstr, ptr)
438 }
439
440 #[test]
441 fn test_dump_buffer_output() {
442 let buffer = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%";
444 let (name_cstr, name_ptr) = make_c_string_with_ptr("hex_dump_test");
445
446 dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
448 }
449
450 #[test]
451 fn test_dump_buffer_exact_format() {
452 let buffer = vec![
454 b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D',
456 b'E', b'F', 0x00, 0x7F, 0x80, 0xFF, 32, 126, 10, 13, 255, 1, 2, 3, b'X', b'Y', b'Z', 0,
458 ];
459
460 let (name_cstr, name_ptr) = make_c_string_with_ptr("exact_format_test");
461
462 dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
464 }
465}