22 个版本
0.6.3 | 2023 年 3 月 22 日 |
---|---|
0.6.1 | 2022 年 4 月 25 日 |
0.6.0 | 2021 年 8 月 27 日 |
0.5.7 | 2021 年 1 月 15 日 |
0.5.6 | 2020 年 1 月 20 日 |
#40 在 内存管理 中
4,083 每月下载量
在 5 crate 中使用
62KB
831 行
checkers
Checkers 是一个简单的 Rust 分配清理器。它通过全局分配器进行连接,可以在集成测试期间对不安全的 Rust 进行合理性检查。由于它通过全局分配器连接,因此不需要任何额外的依赖项,并且适用于所有平台 - 但它可以在验证方面更加有限。
它可以检查以下内容
- 双重释放。
- 内存泄漏。
- 释放未分配的区域。
- 释放已分配区域的部分。
- 释放与不匹配的布局的区域。
- 底层分配器生成的区域遵循请求的布局。即大小和对齐。
- 内存使用情况的详细信息。
- 其他用户定义的条件(《测试》中查看)。
它不能做什么
- 测试多线程代码。由于分配器是全局的,因此难以对每个测试用例的范围进行划分。
- 检测越界访问。
使用方法
将 checkers
作为项目中的 dev-dependency 添加
checkers = "0.6.2"
在一个 测试文件 中替换全局分配器,并使用 #[checkers::test]
将要内存清理的测试用例包装起来
#[global_allocator]
static ALLOCATOR: checkers::Allocator = checkers::Allocator::system();
#[checkers::test]
fn test_allocations() {
let _ = Box::into_raw(Box::new(42));
}
请注意,您必须将测试用例编写为 集成测试,将其添加到您的
tests/
文件夹中,以便隔离全局分配器的使用。
安全性
默认功能集下,此库执行诊断会产生不确定的行为。因此,建议您只使用检查器进行测试,而绝对不要在生产代码中使用。
如果您想避免这种情况,您将不得不禁用realloc
和zeroed
功能,但这也会产生更少有操作性的诊断。
在未来的版本中,这种行为将通过功能标志进行选择,而不是默认启用。
功能
以下是可以用的功能,它们会改变检查器的工作方式。
realloc
- 启用此功能会导致检查器验证realloc操作是否正确实现。即旧区域的字节被忠实地传输到新的、调整大小的区域。由于这可能会产生相当大的性能影响,因此可以禁用它。请注意,这将通过读取未初始化的内存产生不确定的行为(#1),并且仅应启用以在最佳努力的基础上提供诊断。zeroed
- 启用此功能会导致检查器验证对alloc_zeroed
的调用产生所有字节都被设置为零的区域。请注意,如果底层分配器实现得不好,这将产生不确定的行为(#1),因为它可能会读取未初始化的内存。macros
- 启用宏的依赖和重新导出,如#[checkers::test]
。backtrace
- 启用捕获和渲染回溯。如果禁用,则任何包含回溯的字段都将为None
。
示例
建议您使用检查器进行集成测试,这些测试默认位于./tests
目录中。此目录中的每个文件都将作为一个单独的程序编译,因此可以使用全局分配器进行更隔离的使用。
然后,我们在将checkers::Allocator
作为全局分配器安装后,可以在测试中使用#[checkers::test]
属性宏或checkers::with
函数。
#[global_allocator]
static ALLOCATOR: checkers::Allocator = checkers::Allocator::system();
#[checkers::test]
fn test_allocations() {
let _ = Box::into_raw(Box::new(42));
}
上述代码将产生以下测试输出
dangling region: 0x226e5784f30-0x226e5784f40 (size: 16, align: 8).
thread 'test_leak_box' panicked at 'allocation checks failed', tests\leaky_tests.rs:4:1
使用checkers::with
,我们可以执行更详细的诊断
#[global_allocator]
static ALLOCATOR: checkers::Allocator = checkers::Allocator::system();
#[test]
fn test_event_inspection() {
let snapshot = checkers::with(|| {
let _ = vec![1, 2, 3, 4];
});
assert_eq!(2, snapshot.events.len());
assert!(snapshot.events[0].is_alloc_with(|r| r.size >= 16));
assert!(snapshot.events[1].is_free_with(|a| a.size >= 16));
assert_eq!(1, snapshot.events.allocs());
assert_eq!(1, snapshot.events.frees());
assert!(snapshot.events.max_memory_used().unwrap() >= 16);
}
依赖关系
~0–770KB
~16K SLoC