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(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 }
169
170#[cfg(test)]
171mod teec_trace_tests {
172 use super::*;
173 use std::ffi::CString;
174
175 fn make_c_string_with_ptr(s: &str) -> (CString, *const c_char) {
176 let cstr = CString::new(s).expect("Failed to create CString");
177 let ptr = cstr.as_ptr();
178 (cstr, ptr)
179 }
180
181 #[test]
182 fn test_is_printable_ascii() {
183 assert!(is_printable_ascii(b'A'));
185 assert!(is_printable_ascii(b'z'));
186 assert!(is_printable_ascii(b' '));
187 assert!(is_printable_ascii(b'~'));
188
189 assert!(!is_printable_ascii(0));
190 assert!(!is_printable_ascii(31));
191 assert!(!is_printable_ascii(127));
192 assert!(!is_printable_ascii(128));
193 assert!(!is_printable_ascii(255));
194 }
195
196 #[test]
197 fn test_get_level_string() {
198 assert_eq!(get_level_string(TRACE_ERROR), "ERR");
200 assert_eq!(get_level_string(TRACE_INFO), "INF");
201 assert_eq!(get_level_string(TRACE_DEBUG), "DBG");
202 assert_eq!(get_level_string(TRACE_FLOW), "FLW");
203 assert_eq!(get_level_string(0), ""); assert_eq!(get_level_string(999), ""); }
206
207 #[test]
208 fn test_rust_dprintf_valid_input() {
209 let function = Some("test_function");
210 let line = 42;
211 let level = TRACE_INFO;
212 let prefix = "mylib";
213 let message = "This is a test message";
214
215 let (function_cstr, function_ptr) = if let Some(func) = function {
216 make_c_string_with_ptr(func)
217 } else {
218 let cstr = CString::new("").expect("Empty string should always create CString");
220 (cstr, std::ptr::null())
221 };
222
223 let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr(prefix);
224 let (message_cstr, message_ptr) = make_c_string_with_ptr(message);
225
226 let result = rust_dprintf(function_ptr, line, level, prefix_ptr, message_ptr);
227
228 assert!(result > 0);
229 }
230
231 #[test]
232 fn test_rust_dprintf_different_levels() {
233 for (level, _expected_prefix) in [
234 (TRACE_ERROR, "ERR"),
235 (TRACE_INFO, "INF"),
236 (TRACE_DEBUG, "DBG"),
237 (TRACE_FLOW, "FLW"),
238 ] {
239 let function = Some("func");
240 let line = 1;
241 let prefix = "prefix";
242 let message = "message";
243
244 let (function_cstr, function_ptr) = if let Some(func) = function {
245 make_c_string_with_ptr(func)
246 } else {
247 let cstr = CString::new("").expect("Empty string should always create CString");
249 (cstr, std::ptr::null())
250 };
251
252 let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr(prefix);
253 let (message_cstr, message_ptr) = make_c_string_with_ptr(message);
254
255 let result = rust_dprintf(function_ptr, line, level, prefix_ptr, message_ptr);
256
257 assert!(result > 0);
258 }
259 }
260
261 #[test]
262 fn test_rust_dprintf_no_function_info() {
263 let level = TRACE_INFO;
264 let prefix = "prefix";
265 let message = "simple message";
266
267 let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr(prefix);
268 let (message_cstr, message_ptr) = make_c_string_with_ptr(message);
269
270 let result = rust_dprintf(std::ptr::null(), 0, level, prefix_ptr, message_ptr);
271
272 assert_eq!(result, (message.len() + 1) as i32); }
276
277 #[test]
278 fn test_rust_dprintf_null_pointers() {
279 let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr("prefix");
281 let result = rust_dprintf(
282 std::ptr::null(),
283 0,
284 TRACE_INFO,
285 prefix_ptr,
286 std::ptr::null(),
287 );
288 assert_eq!(result, -1);
289
290 let (message_cstr, message_ptr) = make_c_string_with_ptr("message");
291 let result = rust_dprintf(
292 std::ptr::null(),
293 0,
294 TRACE_INFO,
295 std::ptr::null(),
296 message_ptr,
297 );
298 assert_eq!(result, -1);
299
300 let result = rust_dprintf(
301 std::ptr::null(),
302 0,
303 TRACE_INFO,
304 std::ptr::null(),
305 std::ptr::null(),
306 );
307 assert_eq!(result, -1);
308 }
309
310 #[test]
311 fn test_rust_dprintf_invalid_utf8() {
312 let invalid_utf8 = b"valid\xFFpart\0";
314 let cstr = unsafe { CStr::from_bytes_with_nul_unchecked(invalid_utf8) };
315 let ptr = cstr.as_ptr();
316
317 let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr("prefix");
318 let result = rust_dprintf(std::ptr::null(), 0, TRACE_INFO, prefix_ptr, ptr);
319
320 assert_eq!(result, -1);
322 }
323
324 #[test]
325 fn test_thread_id_positive() {
326 let thread_id = get_thread_id();
328 assert!(thread_id > 0);
329 }
330
331 #[test]
332 fn test_integration_style() {
333 let test_cases = vec![
335 (
336 Some("connect"),
337 101,
338 TRACE_ERROR,
339 "network",
340 "Connection failed",
341 ),
342 (
343 Some("process"),
344 205,
345 TRACE_INFO,
346 "worker",
347 "Processing item",
348 ),
349 (
350 Some("validate"),
351 42,
352 TRACE_DEBUG,
353 "parser",
354 "Validating input",
355 ),
356 (Some("loop"), 15, TRACE_FLOW, "main", "Iteration complete"),
357 ];
358
359 for (func, line, level, prefix, msg) in test_cases {
360 let (function_cstr, function_ptr) = if let Some(f) = func {
361 make_c_string_with_ptr(f)
362 } else {
363 let cstr = CString::new("").expect("Empty string should always create CString");
365 (cstr, std::ptr::null())
366 };
367
368 let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr(prefix);
369 let (message_cstr, message_ptr) = make_c_string_with_ptr(msg);
370
371 let result = rust_dprintf(function_ptr, line, level, prefix_ptr, message_ptr);
372
373 assert!(
375 result > 0,
376 "Failed for case: {:?}",
377 (func, line, level, prefix, msg)
378 );
379 }
380 }
381
382 #[test]
383 fn test_performance_no_panics() {
384 let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr("perf");
386 let (message_cstr, message_ptr) = make_c_string_with_ptr("test");
387
388 for _ in 0..1000 {
389 let result = rust_dprintf(std::ptr::null(), 0, TRACE_INFO, prefix_ptr, message_ptr);
390 assert!(result >= 0);
391 }
392 }
393
394 #[test]
396 fn test_dump_buffer_exists() {
397 let buffer = b"Hello, World!";
399 let (name_cstr, name_ptr) = make_c_string_with_ptr("test_buffer");
400
401 dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
403
404 dump_buffer(std::ptr::null(), std::ptr::null(), 0);
406 dump_buffer(name_ptr, std::ptr::null(), 10);
407 dump_buffer(std::ptr::null(), buffer.as_ptr(), buffer.len());
408 }
409
410 #[test]
411 fn test_dump_buffer_edge_cases() {
412 let buffer: [u8; 0] = [];
414 let (name_cstr, name_ptr) = make_c_string_with_ptr("empty");
415
416 dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
417
418 let small_buffer = b"short";
419 let (name2_cstr, name2_ptr) = make_c_string_with_ptr("small");
420
421 dump_buffer(name2_ptr, small_buffer.as_ptr(), small_buffer.len());
422
423 let non_printable = [0u8, 1, 2, 3, 128, 255, 10, 13];
424 let (name3_cstr, name3_ptr) = make_c_string_with_ptr("non_printable");
425
426 dump_buffer(name3_ptr, non_printable.as_ptr(), non_printable.len());
427 }
428}
429
430#[cfg(all(test, any(feature = "debug_level_3", feature = "debug_level_4")))]
431mod dump_buffer_tests {
432 use super::*;
433 use std::ffi::CString;
434
435 fn make_c_string_with_ptr(s: &str) -> (CString, *const c_char) {
437 let cstr = CString::new(s).expect("Failed to create CString");
438 let ptr = cstr.as_ptr();
439 (cstr, ptr)
440 }
441
442 #[test]
443 fn test_dump_buffer_output() {
444 let buffer = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%";
446 let (name_cstr, name_ptr) = make_c_string_with_ptr("hex_dump_test");
447
448 dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
450 }
451
452 #[test]
453 fn test_dump_buffer_exact_format() {
454 let buffer = vec![
456 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',
458 b'E', b'F', 0x00, 0x7F, 0x80, 0xFF, 32, 126, 10, 13, 255, 1, 2, 3, b'X', b'Y', b'Z', 0,
460 ];
461
462 let (name_cstr, name_ptr) = make_c_string_with_ptr("exact_format_test");
463
464 dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
466 }
467}