#stack-trace #trace #print #computer-science

si_trace_print

堆栈缩进跟踪打印;一个Rust库,可以打印带有可选函数名的信息,并缩进到堆栈深度

13个版本

0.3.12 2023年12月9日
0.3.11 2023年12月3日
0.3.10 2023年7月4日
0.3.9 2023年1月17日
0.1.3 2022年9月16日

#94 in 调试

Download history 66/week @ 2024-04-01 23/week @ 2024-04-08 32/week @ 2024-04-15 3/week @ 2024-04-22 2/week @ 2024-04-29 10/week @ 2024-05-06 95/week @ 2024-05-20 74/week @ 2024-05-27 21/week @ 2024-06-03 46/week @ 2024-06-10 58/week @ 2024-06-17 30/week @ 2024-06-24 33/week @ 2024-07-01 60/week @ 2024-07-08 19/week @ 2024-07-15

153每月下载次数
用于 super_speedy_syslog_searc…

MIT 许可证

125KB
2K SLoC

si_trace_print

stack indented trace printing; 一个简单的Rust库,可以打印带有可选函数名的信息,并缩进到堆栈深度。

一个“入门级”跟踪库,以简单手动方式打印函数流程。

Build status docs.rs Rust crates.io codecov.io Commits since License



关于

si_trace_print的目标是一个简单的库,帮助开发者手动审查单个程序运行。它易于使用;不是框架,不需要编译器更改,无需学习新的计算机科学理论即可理解其工作原理。它非常适合在调试构建中轻松添加基本的跟踪(尽管它也可以用于发布构建),而不需要遵守“跟踪框架”或添加不寻常的构建参数。

si_trace_print是一个好的“入门级”跟踪库。

使用

si_trace_print条目添加到项目的Cargo.toml文件中的[dependencies]部分。

跟踪打印示例

最常见的使用可能是带有前缀的调试-only eprintln。

use si_trace_print::{
    den, deo, dex, defn, defo, defx,
};

fn main() {
    den!("hello from main");
    deo!("main will be doing stuff...");
    func1(3);
    deo!("main is done doing stuff.");
    dex!("goodbye from main");
}

fn func1(_var: usize) {
    defn!("({:?})", _var);
    defo!("doing even more stuff...");
    defx!();
}

这将打印到stderr

$ cargo run
→hello from main
 main will be doing stuff...
    →func1: (3)
     func1: doing even more stuff...
    ←func1: 
 main is done doing stuff.
←goodbye from main

如果使用--release构建,则不会编译de语句,因此不会打印任何内容。


使用各种可用的println宏的示例。这些编译成调试和发布构建,并打印到stdout

extern crate si_trace_print;
use si_trace_print::{pf1n, pf2n, pfn, pn, po, px};

fn main() {
    pn!("hello from main");
    pfn!("hello again from main");
    pf1n!("hello again from main!");
    pf2n!("HELLO AGAIN FROM MAIN!!!");
    po!("main will be doing stuff...");
    mod1::mod2::func1(3);
    po!("main is done doing stuff...");
    px!("goodbye from main");
}

mod mod1 {
    pub mod mod2 {
        use si_trace_print::{
            pf1n, pf1o, pf1x, pf1ñ, pf2n, pf2o, pf2x, pf2ñ, pfn, pfo, pfx, pfñ,,
        };
        pub fn func1(var: usize) {
            pf1n!("({:?})", var);
            pf1o!("func1 calling func2...");
            func2(var + 1);
            pf1x!("({:?})", var);
        }
        fn func2(var: usize) {
            pf2n!("({:?})", var);
            pf2o!("calling func3...");
            func3();
            pf2x!("({:?})", var);
        }
        fn func3() {
            pfn!();
            func4();
            pfo!("almost complete...");
            pfx!();
        }
        fn func4() {
            pñ!("func4 is a short function.");
            pfñ!("func4 is a short function.");
            pf1ñ!("func4 is a short function.");
            pf2ñ!("func4 is a short function.");
        }
    }
}

应打印到stdout

→hello from main
→main: hello again from main
→main: hello again from main!
→main: HELLO AGAIN FROM MAIN!!!
 main will be doing stuff...
    →mod2::func1: (3)
     mod2::func1: func1 calling func2...
        →mod1::mod2::func2: (4)
         mod1::mod2::func2: calling func3...
            →func3:
                ↔func4 is a short function.
                ↔func4: func4 is a short function.
                ↔mod2::func4: func4 is a short function.
                ↔mod1::mod2::func4: func4 is a short function.
             func3: almost complete...
            ←func3:
        ←mod1::mod2::func2: (4)
    ←mod2::func1: (3)
 main is done doing stuff...
←goodbye from main

手动设置缩进

库宏的第一次使用将设置“原始”堆栈深度。这随后用于计算缩进偏移。如果此库的第一个使用是在程序中的几个函数之后,则后续打印可能会丢失缩进。

use si_trace_print::{
    pfo, pfn, pfx, pfñ,
};

fn main() {
    func1(3);
    pfx!("goodbye from main (this is not indented!)");
}

fn func1(var: usize) {
    func2(var);
    pfñ!("({:?}) (this is not indented!)", var);
}

fn func2(var: usize) {
    // this is the first call to a si_trace_print function
    // the "original" stack offset will be set from here
    pfn!("({:?})", var);
    pfo!("stack_depth {:?}, stack_offset {:?}", stack_depth(), stack_offset());
    pfx!("({:?})", var);
}

打印的缩进输出不佳

→func2: (3)
 func2: stack_depth 15, stack_offset 0
←func2: (3)
↔func1: (3) (this is not indented!)
←main: goodbye from main (this is not indented!)

在线程开始附近显式调用 stack_offset_set

use si_trace_print::{
    pfo, pfn, pfx, pfñ,
};

fn main() {
    // the "original" stack offset will be set from here
    stack_offset_set(None);
    func1(3);
    pfx!("goodbye from main");
}

fn func1(var: usize) {
    func2(var);
    pfñ!("stack_depth {:?}, stack_offset {:?}", stack_depth(), stack_offset());
}

fn func2(var: usize) {
    pfn!("({:?})", var);
    pfo!("stack_depth {:?}, stack_offset {:?}", stack_depth(), stack_offset());
    pfx!("({:?})", var);
}

这是打印的

            →func2: (3)
             func2: stack_depth 15, stack_offset 2
            ←func2: (3)
        ↔func1: stack_depth 14, stack_offset 1
    ←main: goodbye from main

缩进得到了改善,但缩进得太深了。要传递给 stack_offset_set 的缩进量可能有些不可预测。它取决于构建设置和哪个线程正在运行等因素。在这种情况下,实验表明值 -1 是最好的

// ...
fn main() {
    stack_offset_set(Some(-1));
// ...

这是打印的

        →func2: (3)
         func2: stack_depth 15, stack_offset 1
        ←func2: (3)
    ↔func1: stack_depth 14, stack_offset 0
←main: goodbye from main

缺点

此跟踪函数可能会显著减慢程序。建议使用提供的宏的 调试 版本。

发布构建

函数深度的计算取决于由 backtrace::trace 计数的堆栈帧。在 --release 构建或其他优化配置下,某些函数可能会内联。堆栈帧的数量在函数调用之间可能不会改变。这意味着打印的缩进不会反映函数调用深度。可以通过添加属性 #[inline(never)] 到这些函数来强制避免,尽管 这也不保证能工作

内联属性不能保证函数是否会被内联,但在实践中,#[inline(always)] 将会在绝大多数情况下导致内联。

代码杂乱

这个简单的跟踪辅助程序需要显式语句,这可能会让一些人觉得太乱。

其他跟踪代码包

以下是一些具有不同功能的其他跟踪库。

  • trace 一个像函数包装器一样的过程宏。
  • tracing 一个用于深度程序分析的重量级框架
  • rftrace 使用编译器提供的通过编译器特性 mcount 的函数跟踪。库页面有它自己的部分 替代跟踪器
  • uftrace 最初是一个 C/C++ 跟踪器,现在支持 rust。需要特定安装的库和编译器配置。
  • gsingh93/trace 一个基于宏的跟踪库,类似于 si_trace_print

profile for @JamesThomasMoon on Stack Exchange, a network of free, community-driven Q&A sites

依赖项

~3–4MB
~82K SLoC