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//! TEE 日志跟踪模块。
6//!
7//! 提供 C FFI 兼容的日志输出功能,包括:
8//! - [`rust_dprintf`]:格式化日志输出(级别/线程ID/函数名/行号/消息)
9//! - [`dump_buffer`]:十六进制内存 dump(类似 hexdump -C)
10//! - 4 个日志级别常量:[`TRACE_ERROR`]、[`TRACE_INFO`]、[`TRACE_DEBUG`]、[`TRACE_FLOW`]
11
12use std::ffi::{CStr, c_char, c_int};
13
14use libc::size_t;
15
16/// 错误级别日志,用于报告严重错误。
17pub const TRACE_ERROR: c_int = 1;
18/// 信息级别日志,用于报告一般性运行信息。
19pub const TRACE_INFO: c_int = 2;
20/// 调试级别日志,用于输出详细的调试信息。
21pub const TRACE_DEBUG: c_int = 3;
22/// 流程级别日志,用于跟踪函数进出等执行流程。
23pub const TRACE_FLOW: c_int = 4;
24
25/// 获取当前线程的系统线程 ID(通过 `SYS_gettid` 系统调用)。
26///
27/// 用于日志输出中的线程标识,便于多线程调试。
28fn get_thread_id() -> i32 {
29    unsafe { libc::syscall(libc::SYS_gettid) as i32 }
30}
31
32/// 判断字节是否为可打印ASCII字符(32-126)
33#[allow(dead_code)]
34fn is_printable_ascii(byte: u8) -> bool {
35    (32..=126).contains(&byte)
36}
37
38/// 将日志级别常量转换为对应的字符串表示。
39///
40/// - `TRACE_ERROR` → `"ERR"`
41/// - `TRACE_INFO`  → `"INF"`
42/// - `TRACE_DEBUG` → `"DBG"`
43/// - `TRACE_FLOW`  → `"FLW"`
44/// - 其他值        → `""`
45fn get_level_string(level: c_int) -> &'static str {
46    match level {
47        TRACE_ERROR => "ERR",
48        TRACE_INFO => "INF",
49        TRACE_DEBUG => "DBG",
50        TRACE_FLOW => "FLW",
51        _ => "",
52    }
53}
54
55// TODO: 实现 log_to_file 函数,当定义了 TEEC_LOG_FILE 时,
56// 在函数最后调用 log_to_file 函数将信息写入日志中
57
58/// 格式化输出日志信息到标准输出,格式为:
59/// 级别 \[线程ID\] 前缀:函数名:行号: 消息
60///
61/// # 参数
62/// - function: 函数名(可为null)
63/// - line: 行号(<=0时忽略函数信息)
64/// - level: 日志级别
65/// - prefix: 模块前缀
66/// - message: 日志消息
67///
68/// # 返回值
69/// - 成功时返回输出的字节数,失败时返回-1
70#[expect(clippy::not_unsafe_ptr_arg_deref)]
71#[unsafe(no_mangle)]
72pub extern "C" fn rust_dprintf(
73    function: *const c_char,
74    line: c_int,
75    level: c_int,
76    prefix: *const c_char,
77    message: *const c_char,
78) -> c_int {
79    if prefix.is_null() || message.is_null() {
80        return -1;
81    }
82
83    let msg = {
84        // SAFETY: 已经检查 message 不为空
85        let c_str = unsafe { CStr::from_ptr(message) };
86        match c_str.to_str() {
87            Ok(s) => s,
88            Err(_) => return -1,
89        }
90    };
91
92    let output = if !function.is_null() && line > 0 {
93        // SAFETY: 已经检查 function 不为空
94        // 使用 unwrap_or 提供有意义的默认值,避免静默失败
95        let function_str = unsafe { CStr::from_ptr(function) }
96            .to_str()
97            .unwrap_or("<invalid_utf8>");
98
99        // SAFETY: 已经检查 prefix 不为空
100        let prefix_str = unsafe { CStr::from_ptr(prefix) }
101            .to_str()
102            .unwrap_or("<invalid_utf8>");
103        let thread_id = get_thread_id();
104        let level_str = get_level_string(level);
105
106        format!("{level_str} [{thread_id}] {prefix_str}:{function_str}:{line}: {msg}\n")
107    } else {
108        format!("{msg}\n")
109    };
110
111    print!("{output}");
112
113    output.len() as c_int
114}
115
116/// 十六进制dump函数,用于调试级别3和4
117///
118/// 以十六进制和 ASCII 形式输出内存缓冲区内容,格式类似 hexdump -C
119/// 每行显示 16 字节,左侧为十六进制,右侧为可打印 ASCII 字符
120///
121/// # 参数
122/// - bname: 缓冲区名称,显示在输出开头
123/// - buffer: 待dump的内存缓冲区起始地址
124/// - blen: 缓冲区长度
125#[cfg(any(feature = "debug_level_3", feature = "debug_level_4"))]
126#[expect(clippy::not_unsafe_ptr_arg_deref)]
127#[unsafe(no_mangle)]
128pub extern "C" fn dump_buffer(bname: *const c_char, buffer: *const u8, blen: size_t) {
129    if bname.is_null() || buffer.is_null() || blen == 0 {
130        return;
131    }
132
133    // SAFETY: 已经检查 bname 不为空
134    // 使用 unwrap_or 提供有意义的默认值,便于识别编码问题
135    let bname_str = unsafe { CStr::from_ptr(bname) }
136        .to_str()
137        .unwrap_or("<invalid_utf8>");
138
139    eprintln!("#### {bname_str}");
140
141    // SAFETY: 创建缓冲区切片,已经检查 buffer 不为空且 blen 指定了有效长度
142    let buffer_slice = unsafe { std::slice::from_raw_parts(buffer, blen) };
143    let mut offset = 0;
144
145    while offset < buffer_slice.len() {
146        // 打印十六进制部分(每字节两位十六进制数)
147        for i in 0..16 {
148            if offset + i < buffer_slice.len() {
149                eprint!("{:02x} ", buffer_slice[offset + i]);
150            } else {
151                eprint!("   "); // 填充空格保持对齐
152            }
153
154            if i == 7 {
155                eprint!(" "); // 在8字节后添加额外空格
156            }
157        }
158
159        eprint!(" |");
160
161        // 打印 ASCII
162        for i in 0..16 {
163            if offset + i < buffer_slice.len() {
164                let byte = buffer_slice[offset + i];
165                if is_printable_ascii(byte) {
166                    eprint!("{}", byte as char);
167                } else {
168                    eprint!("."); // 不可打印字符显示为.
169                }
170            }
171        }
172
173        eprintln!("|");
174
175        offset += 16;
176    }
177}
178
179/// dump_buffer 的空实现,用于未启用调试特性时
180///
181/// 保持相同的函数签名但函数体为空,避免链接错误
182#[cfg(not(any(feature = "debug_level_3", feature = "debug_level_4")))]
183#[unsafe(no_mangle)]
184pub extern "C" fn dump_buffer(_bname: *const c_char, _buffer: *const u8, _blen: size_t) {
185    // 空操作,仅在启用 debug_level_3 或 debug_level_4 时才有实际功能
186}
187
188#[cfg(test)]
189mod teec_trace_tests {
190    use super::*;
191    use std::ffi::CString;
192
193    fn make_c_string_with_ptr(s: &str) -> (CString, *const c_char) {
194        let cstr = CString::new(s).expect("Failed to create CString");
195        let ptr = cstr.as_ptr();
196        (cstr, ptr)
197    }
198
199    #[test]
200    fn test_is_printable_ascii() {
201        // 测试各种边界情况
202        assert!(is_printable_ascii(b'A'));
203        assert!(is_printable_ascii(b'z'));
204        assert!(is_printable_ascii(b' '));
205        assert!(is_printable_ascii(b'~'));
206
207        assert!(!is_printable_ascii(0));
208        assert!(!is_printable_ascii(31));
209        assert!(!is_printable_ascii(127));
210        assert!(!is_printable_ascii(128));
211        assert!(!is_printable_ascii(255));
212    }
213
214    #[test]
215    fn test_get_level_string() {
216        // 测试所有预定义的日志级别
217        assert_eq!(get_level_string(TRACE_ERROR), "ERR");
218        assert_eq!(get_level_string(TRACE_INFO), "INF");
219        assert_eq!(get_level_string(TRACE_DEBUG), "DBG");
220        assert_eq!(get_level_string(TRACE_FLOW), "FLW");
221        assert_eq!(get_level_string(0), ""); // 未知级别
222        assert_eq!(get_level_string(999), ""); // 无效级别
223    }
224
225    #[test]
226    fn test_rust_dprintf_valid_input() {
227        let function = Some("test_function");
228        let line = 42;
229        let level = TRACE_INFO;
230        let prefix = "mylib";
231        let message = "This is a test message";
232
233        let (_function_cstr, function_ptr) = if let Some(func) = function {
234            make_c_string_with_ptr(func)
235        } else {
236            // 空字符串永远不会失败,使用 expect 提供清晰的错误信息
237            let cstr = CString::new("").expect("Empty string should always create CString");
238            (cstr, std::ptr::null())
239        };
240
241        let (_prefix_cstr, prefix_ptr) = make_c_string_with_ptr(prefix);
242        let (_message_cstr, message_ptr) = make_c_string_with_ptr(message);
243
244        let result = rust_dprintf(function_ptr, line, level, prefix_ptr, message_ptr);
245
246        assert!(result > 0);
247    }
248
249    #[test]
250    fn test_rust_dprintf_different_levels() {
251        for (level, _expected_prefix) in [
252            (TRACE_ERROR, "ERR"),
253            (TRACE_INFO, "INF"),
254            (TRACE_DEBUG, "DBG"),
255            (TRACE_FLOW, "FLW"),
256        ] {
257            let function = Some("func");
258            let line = 1;
259
260            let (_function_cstr, function_ptr) = if let Some(func) = function {
261                make_c_string_with_ptr(func)
262            } else {
263                // 空字符串永远不会失败,使用 expect 提供清晰的错误信息
264                let cstr = CString::new("").expect("Empty string should always create CString");
265                (cstr, std::ptr::null())
266            };
267
268            let (_prefix_cstr, prefix_ptr) = make_c_string_with_ptr("prefix");
269            let (_message_cstr, message_ptr) = make_c_string_with_ptr("message");
270
271            let result = rust_dprintf(function_ptr, line, level, prefix_ptr, message_ptr);
272
273            assert!(result > 0);
274        }
275    }
276
277    #[test]
278    fn test_rust_dprintf_no_function_info() {
279        let level = TRACE_INFO;
280        let prefix = "prefix";
281        let message = "simple message";
282
283        let (_prefix_cstr, prefix_ptr) = make_c_string_with_ptr(prefix);
284        let (_message_cstr, message_ptr) = make_c_string_with_ptr(message);
285
286        let result = rust_dprintf(std::ptr::null(), 0, level, prefix_ptr, message_ptr);
287
288        // 当 function 为 null 时,输出格式应该只有消息和换行
289        // 返回值应该是消息长度加换行符
290        assert_eq!(result, (message.len() + 1) as i32); // +1 为换行符
291    }
292
293    #[test]
294    fn test_rust_dprintf_null_pointers() {
295        // 测试空指针处理
296        let (_prefix_cstr, prefix_ptr) = make_c_string_with_ptr("prefix");
297        let result = rust_dprintf(
298            std::ptr::null(),
299            0,
300            TRACE_INFO,
301            prefix_ptr,
302            std::ptr::null(),
303        );
304        assert_eq!(result, -1);
305
306        let (_message_cstr, message_ptr) = make_c_string_with_ptr("message");
307        let result = rust_dprintf(
308            std::ptr::null(),
309            0,
310            TRACE_INFO,
311            std::ptr::null(),
312            message_ptr,
313        );
314        assert_eq!(result, -1);
315
316        let result = rust_dprintf(
317            std::ptr::null(),
318            0,
319            TRACE_INFO,
320            std::ptr::null(),
321            std::ptr::null(),
322        );
323        assert_eq!(result, -1);
324    }
325
326    #[test]
327    fn test_rust_dprintf_invalid_utf8() {
328        // 测试无效 UTF-8 输入的处理
329        let invalid_utf8 = b"valid\xFFpart\0";
330        let cstr = unsafe { CStr::from_bytes_with_nul_unchecked(invalid_utf8) };
331        let ptr = cstr.as_ptr();
332
333        let (_prefix_cstr, prefix_ptr) = make_c_string_with_ptr("prefix");
334        let result = rust_dprintf(std::ptr::null(), 0, TRACE_INFO, prefix_ptr, ptr);
335
336        // 应该返回 -1,因为 UTF-8 转换失败
337        assert_eq!(result, -1);
338    }
339
340    #[test]
341    fn test_thread_id_positive() {
342        // 线程 ID 应该是正数
343        let thread_id = get_thread_id();
344        assert!(thread_id > 0);
345    }
346
347    #[test]
348    fn test_integration_style() {
349        // 模拟实际使用场景
350        let test_cases = vec![
351            (
352                Some("connect"),
353                101,
354                TRACE_ERROR,
355                "network",
356                "Connection failed",
357            ),
358            (
359                Some("process"),
360                205,
361                TRACE_INFO,
362                "worker",
363                "Processing item",
364            ),
365            (
366                Some("validate"),
367                42,
368                TRACE_DEBUG,
369                "parser",
370                "Validating input",
371            ),
372            (Some("loop"), 15, TRACE_FLOW, "main", "Iteration complete"),
373        ];
374
375        for (func, line, level, prefix, msg) in test_cases {
376            let (_function_cstr, function_ptr) = if let Some(f) = func {
377                make_c_string_with_ptr(f)
378            } else {
379                // 空字符串永远不会失败,使用 expect 提供清晰的错误信息
380                let cstr = CString::new("").expect("Empty string should always create CString");
381                (cstr, std::ptr::null())
382            };
383
384            let (_prefix_cstr, prefix_ptr) = make_c_string_with_ptr(prefix);
385            let (_message_cstr, message_ptr) = make_c_string_with_ptr(msg);
386
387            let result = rust_dprintf(function_ptr, line, level, prefix_ptr, message_ptr);
388
389            // 基本验证:函数应成功执行
390            assert!(
391                result > 0,
392                "Failed for case: {:?}",
393                (func, line, level, prefix, msg)
394            );
395        }
396    }
397
398    #[test]
399    fn test_performance_no_panics() {
400        // 确保多次调用不会 panic
401        let (_prefix_cstr, prefix_ptr) = make_c_string_with_ptr("perf");
402        let (_message_cstr, message_ptr) = make_c_string_with_ptr("test");
403
404        for _ in 0..1000 {
405            let result = rust_dprintf(std::ptr::null(), 0, TRACE_INFO, prefix_ptr, message_ptr);
406            assert!(result >= 0);
407        }
408    }
409
410    // 测试 dump_buffer 的条件编译
411    #[test]
412    fn test_dump_buffer_exists() {
413        // 验证dump_buffer函数存在且可调用
414        let buffer = b"Hello, World!";
415        let (_name_cstr, name_ptr) = make_c_string_with_ptr("test_buffer");
416
417        // 安全调用,只是验证接口存在
418        dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
419
420        // 测试空指针和零长度
421        dump_buffer(std::ptr::null(), std::ptr::null(), 0);
422        dump_buffer(name_ptr, std::ptr::null(), 10);
423        dump_buffer(std::ptr::null(), buffer.as_ptr(), buffer.len());
424    }
425
426    #[test]
427    fn test_dump_buffer_edge_cases() {
428        // 测试 dump_buffer 的边界情况
429        let buffer: [u8; 0] = [];
430        let (_name_cstr, name_ptr) = make_c_string_with_ptr("empty");
431
432        dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
433
434        let small_buffer = b"short";
435        let (_name2_cstr, name2_ptr) = make_c_string_with_ptr("small");
436
437        dump_buffer(name2_ptr, small_buffer.as_ptr(), small_buffer.len());
438
439        let non_printable = [0u8, 1, 2, 3, 128, 255, 10, 13];
440        let (_name3_cstr, name3_ptr) = make_c_string_with_ptr("non_printable");
441
442        dump_buffer(name3_ptr, non_printable.as_ptr(), non_printable.len());
443    }
444}
445
446#[cfg(all(test, any(feature = "debug_level_3", feature = "debug_level_4")))]
447mod dump_buffer_tests {
448    use super::*;
449    use std::ffi::CString;
450
451    // 测试辅助函数
452    fn make_c_string_with_ptr(s: &str) -> (CString, *const c_char) {
453        let cstr = CString::new(s).expect("Failed to create CString");
454        let ptr = cstr.as_ptr();
455        (cstr, ptr)
456    }
457
458    #[test]
459    fn test_dump_buffer_output() {
460        // 测试实际的dump_buffer输出格式(仅在启用相关特性时)
461        let buffer = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%";
462        let (_name_cstr, name_ptr) = make_c_string_with_ptr("hex_dump_test");
463
464        // 这个测试主要确保函数不 panic
465        dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
466    }
467
468    #[test]
469    fn test_dump_buffer_exact_format() {
470        // 测试精确的格式输出
471        let buffer = vec![
472            // 第一行:可打印ASCII
473            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',
474            b'E', b'F', // 第二行:混合内容
475            0x00, 0x7F, 0x80, 0xFF, 32, 126, 10, 13, 255, 1, 2, 3, b'X', b'Y', b'Z', 0,
476        ];
477
478        let (_name_cstr, name_ptr) = make_c_string_with_ptr("exact_format_test");
479
480        // 这个测试主要确保函数不 panic
481        dump_buffer(name_ptr, buffer.as_ptr(), buffer.len());
482    }
483}