#![allow(unused_variables)]
use std::ffi::CStr;
use libc::{c_char, c_int, size_t};
pub const TRACE_ERROR: c_int = 1;
pub const TRACE_INFO: c_int = 2;
pub const TRACE_DEBUG: c_int = 3;
pub const TRACE_FLOW: c_int = 4;
fn get_thread_id() -> i32 {
unsafe { libc::syscall(libc::SYS_gettid) as i32 }
}
#[allow(dead_code)]
fn is_printable_ascii(byte: u8) -> bool {
(32..=126).contains(&byte)
}
fn get_level_string(level: c_int) -> &'static str {
match level {
TRACE_ERROR => "ERR",
TRACE_INFO => "INF",
TRACE_DEBUG => "DBG",
TRACE_FLOW => "FLW",
_ => "",
}
}
#[expect(clippy::not_unsafe_ptr_arg_deref)]
#[unsafe(no_mangle)]
pub extern "C" fn rust_dprintf(
function: *const c_char,
line: c_int,
level: c_int,
prefix: *const c_char,
message: *const c_char,
) -> c_int {
if prefix.is_null() || message.is_null() {
return -1;
}
let msg = {
let c_str = unsafe { CStr::from_ptr(message) };
match c_str.to_str() {
Ok(s) => s,
Err(_) => return -1,
}
};
let output = if !function.is_null() && line > 0 {
let function_str = unsafe { CStr::from_ptr(function) }
.to_str()
.unwrap_or("<invalid_utf8>");
let prefix_str = unsafe { CStr::from_ptr(prefix) }
.to_str()
.unwrap_or("<invalid_utf8>");
let thread_id = get_thread_id();
let level_str = get_level_string(level);
format!("{level_str} [{thread_id}] {prefix_str}:{function_str}:{line}: {msg}\n")
} else {
format!("{msg}\n")
};
print!("{output}");
output.len() as c_int
}
#[cfg(any(feature = "debug_level_3", feature = "debug_level_4"))]
#[expect(clippy::not_unsafe_ptr_arg_deref)]
#[unsafe(no_mangle)]
pub extern "C" fn dump_buffer(bname: *const c_char, buffer: *const u8, blen: size_t) {
if bname.is_null() || buffer.is_null() || blen == 0 {
return;
}
let bname_str = unsafe { CStr::from_ptr(bname) }
.to_str()
.unwrap_or("<invalid_utf8>");
eprintln!("#### {bname_str}");
let buffer_slice = unsafe { std::slice::from_raw_parts(buffer, blen) };
let mut offset = 0;
while offset < buffer_slice.len() {
for i in 0..16 {
if offset + i < buffer_slice.len() {
eprint!("{:02x} ", buffer_slice[offset + i]);
} else {
eprint!(" "); }
if i == 7 {
eprint!(" "); }
}
eprint!(" |");
for i in 0..16 {
if offset + i < buffer_slice.len() {
let byte = buffer_slice[offset + i];
if is_printable_ascii(byte) {
eprint!("{}", byte as char);
} else {
eprint!("."); }
}
}
eprintln!("|");
offset += 16;
}
}
#[cfg(not(any(feature = "debug_level_3", feature = "debug_level_4")))]
#[unsafe(no_mangle)]
pub extern "C" fn dump_buffer(_bname: *const c_char, _buffer: *const u8, _blen: size_t) {
}
#[cfg(test)]
mod teec_trace_tests {
use super::*;
use std::ffi::CString;
fn make_c_string_with_ptr(s: &str) -> (CString, *const c_char) {
let cstr = CString::new(s).expect("Failed to create CString");
let ptr = cstr.as_ptr();
(cstr, ptr)
}
#[test]
fn test_is_printable_ascii() {
assert!(is_printable_ascii(b'A'));
assert!(is_printable_ascii(b'z'));
assert!(is_printable_ascii(b' '));
assert!(is_printable_ascii(b'~'));
assert!(!is_printable_ascii(0));
assert!(!is_printable_ascii(31));
assert!(!is_printable_ascii(127));
assert!(!is_printable_ascii(128));
assert!(!is_printable_ascii(255));
}
#[test]
fn test_get_level_string() {
assert_eq!(get_level_string(TRACE_ERROR), "ERR");
assert_eq!(get_level_string(TRACE_INFO), "INF");
assert_eq!(get_level_string(TRACE_DEBUG), "DBG");
assert_eq!(get_level_string(TRACE_FLOW), "FLW");
assert_eq!(get_level_string(0), ""); assert_eq!(get_level_string(999), ""); }
#[test]
fn test_rust_dprintf_valid_input() {
let function = Some("test_function");
let line = 42;
let level = TRACE_INFO;
let prefix = "mylib";
let message = "This is a test message";
let (function_cstr, function_ptr) = if let Some(func) = function {
make_c_string_with_ptr(func)
} else {
let cstr = CString::new("").expect("Empty string should always create CString");
(cstr, std::ptr::null())
};
let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr(prefix);
let (message_cstr, message_ptr) = make_c_string_with_ptr(message);
let result = rust_dprintf(function_ptr, line, level, prefix_ptr, message_ptr);
assert!(result > 0);
}
#[test]
fn test_rust_dprintf_different_levels() {
for (level, _expected_prefix) in [
(TRACE_ERROR, "ERR"),
(TRACE_INFO, "INF"),
(TRACE_DEBUG, "DBG"),
(TRACE_FLOW, "FLW"),
] {
let function = Some("func");
let line = 1;
let prefix = "prefix";
let message = "message";
let (function_cstr, function_ptr) = if let Some(func) = function {
make_c_string_with_ptr(func)
} else {
let cstr = CString::new("").expect("Empty string should always create CString");
(cstr, std::ptr::null())
};
let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr(prefix);
let (message_cstr, message_ptr) = make_c_string_with_ptr(message);
let result = rust_dprintf(function_ptr, line, level, prefix_ptr, message_ptr);
assert!(result > 0);
}
}
#[test]
fn test_rust_dprintf_no_function_info() {
let level = TRACE_INFO;
let prefix = "prefix";
let message = "simple message";
let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr(prefix);
let (message_cstr, message_ptr) = make_c_string_with_ptr(message);
let result = rust_dprintf(std::ptr::null(), 0, level, prefix_ptr, message_ptr);
assert_eq!(result, (message.len() + 1) as i32); }
#[test]
fn test_rust_dprintf_null_pointers() {
let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr("prefix");
let result = rust_dprintf(
std::ptr::null(),
0,
TRACE_INFO,
prefix_ptr,
std::ptr::null(),
);
assert_eq!(result, -1);
let (message_cstr, message_ptr) = make_c_string_with_ptr("message");
let result = rust_dprintf(
std::ptr::null(),
0,
TRACE_INFO,
std::ptr::null(),
message_ptr,
);
assert_eq!(result, -1);
let result = rust_dprintf(
std::ptr::null(),
0,
TRACE_INFO,
std::ptr::null(),
std::ptr::null(),
);
assert_eq!(result, -1);
}
#[test]
fn test_rust_dprintf_invalid_utf8() {
let invalid_utf8 = b"valid\xFFpart\0";
let cstr = unsafe { CStr::from_bytes_with_nul_unchecked(invalid_utf8) };
let ptr = cstr.as_ptr();
let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr("prefix");
let result = rust_dprintf(std::ptr::null(), 0, TRACE_INFO, prefix_ptr, ptr);
assert_eq!(result, -1);
}
#[test]
fn test_thread_id_positive() {
let thread_id = get_thread_id();
assert!(thread_id > 0);
}
#[test]
fn test_integration_style() {
let test_cases = vec![
(
Some("connect"),
101,
TRACE_ERROR,
"network",
"Connection failed",
),
(
Some("process"),
205,
TRACE_INFO,
"worker",
"Processing item",
),
(
Some("validate"),
42,
TRACE_DEBUG,
"parser",
"Validating input",
),
(Some("loop"), 15, TRACE_FLOW, "main", "Iteration complete"),
];
for (func, line, level, prefix, msg) in test_cases {
let (function_cstr, function_ptr) = if let Some(f) = func {
make_c_string_with_ptr(f)
} else {
let cstr = CString::new("").expect("Empty string should always create CString");
(cstr, std::ptr::null())
};
let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr(prefix);
let (message_cstr, message_ptr) = make_c_string_with_ptr(msg);
let result = rust_dprintf(function_ptr, line, level, prefix_ptr, message_ptr);
assert!(
result > 0,
"Failed for case: {:?}",
(func, line, level, prefix, msg)
);
}
}
#[test]
fn test_performance_no_panics() {
let (prefix_cstr, prefix_ptr) = make_c_string_with_ptr("perf");
let (message_cstr, message_ptr) = make_c_string_with_ptr("test");
for _ in 0..1000 {
let result = rust_dprintf(std::ptr::null(), 0, TRACE_INFO, prefix_ptr, message_ptr);
assert!(result >= 0);
}
}
#[test]
fn test_dump_buffer_exists() {
let buffer = b"Hello, World!";
let (name_cstr, name_ptr) = make_c_string_with_ptr("test_buffer");
dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
dump_buffer(std::ptr::null(), std::ptr::null(), 0);
dump_buffer(name_ptr, std::ptr::null(), 10);
dump_buffer(std::ptr::null(), buffer.as_ptr(), buffer.len());
}
#[test]
fn test_dump_buffer_edge_cases() {
let buffer: [u8; 0] = [];
let (name_cstr, name_ptr) = make_c_string_with_ptr("empty");
dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
let small_buffer = b"short";
let (name2_cstr, name2_ptr) = make_c_string_with_ptr("small");
dump_buffer(name2_ptr, small_buffer.as_ptr(), small_buffer.len());
let non_printable = [0u8, 1, 2, 3, 128, 255, 10, 13];
let (name3_cstr, name3_ptr) = make_c_string_with_ptr("non_printable");
dump_buffer(name3_ptr, non_printable.as_ptr(), non_printable.len());
}
}
#[cfg(all(test, any(feature = "debug_level_3", feature = "debug_level_4")))]
mod dump_buffer_tests {
use super::*;
use std::ffi::CString;
fn make_c_string_with_ptr(s: &str) -> (CString, *const c_char) {
let cstr = CString::new(s).expect("Failed to create CString");
let ptr = cstr.as_ptr();
(cstr, ptr)
}
#[test]
fn test_dump_buffer_output() {
let buffer = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%";
let (name_cstr, name_ptr) = make_c_string_with_ptr("hex_dump_test");
dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
}
#[test]
fn test_dump_buffer_exact_format() {
let buffer = vec![
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',
b'E', b'F', 0x00, 0x7F, 0x80, 0xFF, 32, 126, 10, 13, 255, 1, 2, 3, b'X', b'Y', b'Z', 0,
];
let (name_cstr, name_ptr) = make_c_string_with_ptr("exact_format_test");
dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
}
}