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 调试
153每月下载次数
用于 super_speedy_syslog_searc…
125KB
2K SLoC
si_trace_print
stack indented trace printing; 一个简单的Rust库,可以打印带有可选函数名的信息,并缩进到堆栈深度。
一个“入门级”跟踪库,以简单手动方式打印函数流程。
关于
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ñ, pñ,
};
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
依赖项
~3–4MB
~82K SLoC