Skip to main content

cc_teec/
teec_trace.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (C) 2025-2026 KylinSoft Co., Ltd. <https://www.kylinos.cn/>
3// See LICENSES for license details.
4
5#![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/// 判断字节是否为可打印ASCII字符(32-126)
21#[allow(dead_code)]
22fn is_printable_ascii(byte: u8) -> bool {
23    (32..=126).contains(&byte)
24}
25
26/// 将日志级别常量转换为对应的字符串表示
27fn 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// TODO: 实现 log_to_file 函数,当定义了 TEEC_LOG_FILE 时,
38// 在函数最后调用 log_to_file 函数将信息写入日志中
39
40/// 格式化输出日志信息到标准输出,格式为:
41/// 级别 \[线程ID\] 前缀:函数名:行号: 消息
42///
43/// # 参数
44/// - function: 函数名(可为null)
45/// - line: 行号(<=0时忽略函数信息)
46/// - level: 日志级别
47/// - prefix: 模块前缀
48/// - message: 日志消息
49///
50/// # 返回值
51/// - 成功时返回输出的字节数,失败时返回-1
52#[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        // SAFETY: 已经检查 message 不为空
67        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        // SAFETY: 已经检查 function 不为空
76        // 使用 unwrap_or 提供有意义的默认值,避免静默失败
77        let function_str = unsafe { CStr::from_ptr(function) }
78            .to_str()
79            .unwrap_or("<invalid_utf8>");
80
81        // SAFETY: 已经检查 prefix 不为空
82        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/// 十六进制dump函数,用于调试级别3和4
99///
100/// 以十六进制和 ASCII 形式输出内存缓冲区内容,格式类似 hexdump -C
101/// 每行显示 16 字节,左侧为十六进制,右侧为可打印 ASCII 字符
102///
103/// # 参数
104/// - bname: 缓冲区名称,显示在输出开头
105/// - buffer: 待dump的内存缓冲区起始地址
106/// - blen: 缓冲区长度
107#[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    // SAFETY: 已经检查 bname 不为空
116    // 使用 unwrap_or 提供有意义的默认值,便于识别编码问题
117    let bname_str = unsafe { CStr::from_ptr(bname) }
118        .to_str()
119        .unwrap_or("<invalid_utf8>");
120
121    eprintln!("#### {bname_str}");
122
123    // SAFETY: 创建缓冲区切片,已经检查 buffer 不为空且 blen 指定了有效长度
124    let buffer_slice = unsafe { std::slice::from_raw_parts(buffer, blen) };
125    let mut offset = 0;
126
127    while offset < buffer_slice.len() {
128        // 打印十六进制部分(每字节两位十六进制数)
129        for i in 0..16 {
130            if offset + i < buffer_slice.len() {
131                eprint!("{:02x} ", buffer_slice[offset + i]);
132            } else {
133                eprint!("   "); // 填充空格保持对齐
134            }
135
136            if i == 7 {
137                eprint!(" "); // 在8字节后添加额外空格
138            }
139        }
140
141        eprint!(" |");
142
143        // 打印 ASCII
144        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!("."); // 不可打印字符显示为.
151                }
152            }
153        }
154
155        eprintln!("|");
156
157        offset += 16;
158    }
159}
160
161/// dump_buffer 的空实现,用于未启用调试特性时
162///
163/// 保持相同的函数签名但函数体为空,避免链接错误
164#[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        // 测试各种边界情况
182        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        // 测试所有预定义的日志级别
197        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), ""); // 未知级别
202        assert_eq!(get_level_string(999), ""); // 无效级别
203    }
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            // 空字符串永远不会失败,使用 expect 提供清晰的错误信息
217            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                // 空字符串永远不会失败,使用 expect 提供清晰的错误信息
246                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        // 当 function 为 null 时,输出格式应该只有消息和换行
271        // 返回值应该是消息长度加换行符
272        assert_eq!(result, (message.len() + 1) as i32); // +1 为换行符
273    }
274
275    #[test]
276    fn test_rust_dprintf_null_pointers() {
277        // 测试空指针处理
278        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        // 测试无效 UTF-8 输入的处理
311        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        // 应该返回 -1,因为 UTF-8 转换失败
319        assert_eq!(result, -1);
320    }
321
322    #[test]
323    fn test_thread_id_positive() {
324        // 线程 ID 应该是正数
325        let thread_id = get_thread_id();
326        assert!(thread_id > 0);
327    }
328
329    #[test]
330    fn test_integration_style() {
331        // 模拟实际使用场景
332        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                // 空字符串永远不会失败,使用 expect 提供清晰的错误信息
362                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            // 基本验证:函数应成功执行
372            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        // 确保多次调用不会 panic
383        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    // 测试 dump_buffer 的条件编译
393    #[test]
394    fn test_dump_buffer_exists() {
395        // 验证dump_buffer函数存在且可调用
396        let buffer = b"Hello, World!";
397        let (name_cstr, name_ptr) = make_c_string_with_ptr("test_buffer");
398
399        // 安全调用,只是验证接口存在
400        dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
401
402        // 测试空指针和零长度
403        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        // 测试 dump_buffer 的边界情况
411        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    // 测试辅助函数
434    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        // 测试实际的dump_buffer输出格式(仅在启用相关特性时)
443        let buffer = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%";
444        let (name_cstr, name_ptr) = make_c_string_with_ptr("hex_dump_test");
445
446        // 这个测试主要确保函数不 panic
447        dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
448    }
449
450    #[test]
451    fn test_dump_buffer_exact_format() {
452        // 测试精确的格式输出
453        let buffer = vec![
454            // 第一行:可打印ASCII
455            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', // 第二行:混合内容
457            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        // 这个测试主要确保函数不 panic
463        dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
464    }
465}