Rustを使っているとそれほどメモリリークを気にすることはないですが、それでも必要になるときがちょこちょこあります。Cと連携したりとかunsafeなコードをガンガン使ったりとか。
そんなときはもちろん(?)valgrindを使っていくわけですが、プログラム内でvalgrindにアクセスしたいなんてときもあるわけです。(ほんとか?)
そこでvalgrindはClient Requestという仕組みでvalgrind内部でもってる統計情報とかにvalgrind上で実行しているプログラムからアクセスするためのAPIを提供しています。
このAPIはCから使う前提でAPIが書かれており、マクロを使いマクロ!wというコードなので一見それ以外の言語から触ることができないように見えるのですが、コアはvalgrindに対する特殊なhypercall呼び出しを実行してるだけなのでインラインアセンブラを書けばどの言語からも呼び出すことができます。
Rustには2つの実装があります。
実装してるAPIが多いのはvgrs、対応しているアーキテクチャが多いのはlibvalgrind_requestといったかんじです。
ちょっとvgrsのほうを試してみましょう。ちなみにvgrsは本家のレポジトリが更新されておらずfork先のブランチでなければコンパイルできないのでそちらを使います。
Cargo.tomlをこのようにして、
[package]
name = "vgrs"
version = "0.1.0"
authors = ["Hiroki Noda <kubo39[at]gmail.com>"]
[dependencies]
vgrs = { git = "https://github.com/lummax/vgrs.git", rev = "5252a84bcdc266132181df908f06" }
例にあるコードを少し修正して実行させてみます。
#![feature(core_intrinsics)]
extern crate vgrs;
use vgrs::valgrind;
fn main() {
unsafe {
assert!(valgrind::count_errors() == 0);
let x: u8 = std::intrinsics::uninit();
println!("{:?}", x);
assert!(valgrind::count_errors() > 0);
}
}
まずvalgrindを使わないで実行させてみます。
$ ./target/debug/vgrs
0
thread 'main' panicked at 'assertion failed: valgrind::count_errors() > 0', src/main.rs:12
note: Run with `RUST_BACKTRACE=1` for a backtrace.
2回目のassert!で失敗してpanicしました。
次にvalgrind上でプログラムを実行させてみます。
$ valgrind --leak-check=full ./target/debug/vgrs
$ valgrind --leak-check=full ./target/debug/vgrs [kubo39:vgrs][git:master]
==5400== Memcheck, a memory error detector
==5400== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==5400== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==5400== Command: ./target/debug/vgrs
==5400==
==5400== Conditional jump or move depends on uninitialised value(s)
==5400== at 0x13ED82: fmt (num.rs:234)
...
==5400== Address 0x6220000 is in a rw- anonymous segment
==5400==
0
==5400==
==5400== HEAP SUMMARY:
==5400== in use at exit: 0 bytes in 0 blocks
==5400== total heap usage: 7 allocs, 7 frees, 2,032 bytes allocated
==5400==
==5400== All heap blocks were freed -- no leaks are possible
==5400==
==5400== For counts of detected and suppressed errors, rerun with: -v
==5400== Use --track-origins=yes to see where uninitialised values come from
==5400== ERROR SUMMARY: 4 errors from 4 contexts (suppressed: 0 from 0)
メモリの未初期化まわりでエラーが4つでたので、2つめのassertの条件を通るようになりpanicしなくなりました。