使用 Valgrind 运行 cargo test

使用 Valgrind 运行 cargo test

💡 原文英文,约1100词,阅读约需4分钟。
📝

内容提要

本文介绍用Valgrind检测Rust程序内存错误。Rust虽内存安全,但通过FFI调用C/C++需使用unsafe,可能引入内存问题。作者以泄漏内存的示例项目演示三种方式:直接用Valgrind运行编译后的二进制或测试二进制;用cargo-valgrind子命令简化cargo test,检测到泄漏会使测试失败;通过CARGO_TARGET_*_RUNNER设置自定义runner,让cargo test在Valgrind下运行。

🔎

延伸解读

为什么 Rust 项目仍需 Valgrind

Rust 编译器能在编译期拦截多数内存错误,但通过 FFI 调用 C/C++ 时需使用 unsafe,可能引入内存泄漏或越界等问题。文章以故意泄漏内存的示例说明,即使测试通过,Valgrind 仍能报告“definitely lost”的堆内存。因此,涉及 unsafe 或外部代码的 Rust 项目,可将 Valgrind 作为补充检查手段。

三种运行方式的取舍

直接对编译后的二进制或测试二进制运行 Valgrind 最灵活,但测试二进制路径随构建变化,手动查找繁琐。cargo-valgrind 子命令简化了 cargo test,检测到泄漏会使测试失败,并输出带颜色的调用栈,但截至 v2.1.0 无法只输出原始 Valgrind 文本。自定义 runner 则通过环境变量让 cargo test 在 Valgrind 下运行,适合需要原始输出或集成到现有流程的场景。

让内存错误导致测试失败

默认情况下,Valgrind 报告泄漏但测试仍显示通过。文章演示了两种让测试失败的方式:cargo-valgrind 会自动将泄漏视为错误;使用自定义 runner 时,需在 Valgrind 命令中加入 --error-exitcode=1,使检测到错误时返回非零退出码。这样 CI 或本地测试才能及时拦截内存问题。

自定义 runner 的通用性

Cargo 的自定义 target runner 原本用于交叉测试,例如在 Linux 上编译 Windows 目标并用 wine 运行测试。文章利用同一机制,通过设置 CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER 为 valgrind 命令,让 cargo test 自动在 Valgrind 下执行。这种方法不依赖额外子命令,但需注意目标三元组名称要与实际平台匹配。

❓

Q&A

Rust 不是内存安全的吗,为什么还需要用 Valgrind 检测内存错误?

Rust 编译器在编译期能捕获许多内存错误,但当通过 FFI 调用 C/C++ 代码时,往往需要使用 unsafe,这可能引入内存不安全问题。因此,Valgrind 对于处理 unsafe Rust 代码是有价值的工具。

如何直接用 Valgrind 运行编译后的 Rust 二进制程序?

先使用 cargo build 编译项目,然后直接运行 valgrind target/debug/example(将 example 替换为你的二进制名称)。Valgrind 会输出内存使用和泄漏摘要。

怎样用 Valgrind 运行 Rust 的单元测试?

先运行 cargo test 编译测试,然后找到测试二进制路径(如 target/debug/deps/example-64baa11f6262b62f),再执行 valgrind 加上该路径。但手动查找路径较繁琐,尤其有多个测试二进制时。

cargo-valgrind 是什么?如何使用它来运行测试?

cargo-valgrind 是一个 cargo 子命令,扩展了 cargo 的能力,可以直接对任何 crate 可执行文件运行 Valgrind。使用 cargo valgrind test 即可在 Valgrind 下运行测试,它会提供彩色输出,并在检测到内存错误时使测试失败。但截至 v2.1.0 版本,它无法仅输出原始的 Valgrind 输出。

如何通过 Cargo runner 配置让 cargo test 自动在 Valgrind 下运行?

可以设置环境变量 CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="valgrind --error-exitcode=1",然后运行 cargo test。这样 cargo 会使用指定的 runner 来运行测试二进制,Valgrind 会在检测到错误时以非零退出码使测试失败。

cargo-valgrind 检测到内存泄漏时,测试会失败吗?

是的,cargo-valgrind 在检测到内存错误(包括泄漏)时会使测试失败,并输出错误信息,例如“error: test failed, to rerun pass '--bin example'”。

🏷️

标签

➡️

继续阅读