Valgrind(ヴァルグリンド)の使い方
- 1 Valgrindとは?
- 2 検証環境
- 3 インストール方法
- 4 オプション一覧
- 5 メモリリークを検出する方法
- 6 解放済みメモリー領域へのポインタを誤って使ってしまう問題の検出方法
- 7 確保したメモリ領域外への不正アクセスの検出方法
- Z 参考図書
1 Valgrindとは?
メモリリーク等のバグを検出するツールです。
2 検証環境
CentOS版数は以下のとおりです。仮想マシンは最小構成で作成しました。
[root@server ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core)
カーネル版数は以下のとおりです。
[root@server ~]# uname -r 3.10.0-1160.el7.x86_64
3 インストール方法
valgrindパッケージをインストールします。
[root@server ~]# yum -y install valgrind
valgrindの版数を確認します。
[root@server ~]# valgrind --version valgrind-3.15.0
4 オプション一覧
オプションは以下のとおりです
[root@server ~]# valgrind --help
usage: valgrind [options] prog-and-args
tool-selection option, with default in [ ]:
--tool=<name> use the Valgrind tool named <name> [memcheck]
basic user options for all Valgrind tools, with defaults in [ ]:
-h --help show this message
--help-debug show this message, plus debugging options
--version show version
-q --quiet run silently; only print error msgs
-v --verbose be more verbose -- show misc extra info
--trace-children=no|yes Valgrind-ise child processes (follow execve)? [no]
--trace-children-skip=patt1,patt2,... specifies a list of executables
that --trace-children=yes should not trace into
--trace-children-skip-by-arg=patt1,patt2,... same as --trace-children-skip=
but check the argv[] entries for children, rather
than the exe name, to make a follow/no-follow decision
--child-silent-after-fork=no|yes omit child output between fork & exec? [no]
--vgdb=no|yes|full activate gdbserver? [yes]
full is slower but provides precise watchpoint/step
--vgdb-error=<number> invoke gdbserver after <number> errors [999999999]
to get started quickly, use --vgdb-error=0
and follow the on-screen directions
--vgdb-stop-at=event1,event2,... invoke gdbserver for given events [none]
where event is one of:
startup exit valgrindabexit all none
--track-fds=no|yes track open file descriptors? [no]
--time-stamp=no|yes add timestamps to log messages? [no]
--log-fd=<number> log messages to file descriptor [2=stderr]
--log-file=<file> log messages to <file>
--log-socket=ipaddr:port log messages to socket ipaddr:port
user options for Valgrind tools that report errors:
--xml=yes emit error output in XML (some tools only)
--xml-fd=<number> XML output to file descriptor
--xml-file=<file> XML output to <file>
--xml-socket=ipaddr:port XML output to socket ipaddr:port
--xml-user-comment=STR copy STR verbatim into XML output
--demangle=no|yes automatically demangle C++ names? [yes]
--num-callers=<number> show <number> callers in stack traces [12]
--error-limit=no|yes stop showing new errors if too many? [yes]
--exit-on-first-error=no|yes exit code on the first error found? [no]
--error-exitcode=<number> exit code to return if errors found [0=disable]
--error-markers=<begin>,<end> add lines with begin/end markers before/after
each error output in plain text mode [none]
--show-error-list=no|yes show detected errors list and
suppression counts at exit [no]
-s same as --show-error-list=yes
--keep-debuginfo=no|yes Keep symbols etc for unloaded code [no]
This allows saved stack traces (e.g. memory leaks)
to include file/line info for code that has been
dlclose'd (or similar)
--show-below-main=no|yes continue stack traces below main() [no]
--default-suppressions=yes|no
load default suppressions [yes]
--suppressions=<filename> suppress errors described in <filename>
--gen-suppressions=no|yes|all print suppressions for errors? [no]
--input-fd=<number> file descriptor for input [0=stdin]
--dsymutil=no|yes run dsymutil on Mac OS X when helpful? [yes]
--max-stackframe=<number> assume stack switch for SP changes larger
than <number> bytes [2000000]
--main-stacksize=<number> set size of main thread's stack (in bytes)
[min(max(current 'ulimit' value,1MB),16MB)]
user options for Valgrind tools that replace malloc:
--alignment=<number> set minimum alignment of heap allocations [16]
--redzone-size=<number> set minimum size of redzones added before/after
heap blocks (in bytes). [16]
--xtree-memory=none|allocs|full profile heap memory in an xtree [none]
and produces a report at the end of the execution
none: no profiling, allocs: current allocated
size/blocks, full: profile current and cumulative
allocated size/blocks and freed size/blocks.
--xtree-memory-file=<file> xtree memory report file [xtmemory.kcg.%p]
uncommon user options for all Valgrind tools:
--fullpath-after= (with nothing after the '=')
show full source paths in call stacks
--fullpath-after=string like --fullpath-after=, but only show the
part of the path after 'string'. Allows removal
of path prefixes. Use this flag multiple times
to specify a set of prefixes to remove.
--extra-debuginfo-path=path absolute path to search for additional
debug symbols, in addition to existing default
well known search paths.
--debuginfo-server=ipaddr:port also query this server
(valgrind-di-server) for debug symbols
--allow-mismatched-debuginfo=no|yes [no]
for the above two flags only, accept debuginfo
objects that don't "match" the main object
--smc-check=none|stack|all|all-non-file [all-non-file]
checks for self-modifying code: none, only for
code found in stacks, for all code, or for all
code except that from file-backed mappings
--read-inline-info=yes|no read debug info about inlined function calls
and use it to do better stack traces.
[yes] on Linux/Android/Solaris for the tools
Memcheck/Massif/Helgrind/DRD only.
[no] for all other tools and platforms.
--read-var-info=yes|no read debug info on stack and global variables
and use it to print better error messages in
tools that make use of it (Memcheck, Helgrind,
DRD) [no]
--vgdb-poll=<number> gdbserver poll max every <number> basic blocks [5000]
--vgdb-shadow-registers=no|yes let gdb see the shadow registers [no]
--vgdb-prefix=<prefix> prefix for vgdb FIFOs [/tmp/vgdb-pipe]
--run-libc-freeres=no|yes free up glibc memory at exit on Linux? [yes]
--run-cxx-freeres=no|yes free up libstdc++ memory at exit on Linux
and Solaris? [yes]
--sim-hints=hint1,hint2,... activate unusual sim behaviours [none]
where hint is one of:
lax-ioctls lax-doors fuse-compatible enable-outer
no-inner-prefix no-nptl-pthread-stackcache fallback-llsc none
--fair-sched=no|yes|try schedule threads fairly on multicore systems [no]
--kernel-variant=variant1,variant2,...
handle non-standard kernel variants [none]
where variant is one of:
bproc android-no-hw-tls
android-gpu-sgx5xx android-gpu-adreno3xx none
--merge-recursive-frames=<number> merge frames between identical
program counters in max <number> frames) [0]
--num-transtab-sectors=<number> size of translated code cache [32]
more sectors may increase performance, but use more memory.
--avg-transtab-entry-size=<number> avg size in bytes of a translated
basic block [0, meaning use tool provided default]
--aspace-minaddr=0xPP avoid mapping memory below 0xPP [guessed]
--valgrind-stacksize=<number> size of valgrind (host) thread's stack
(in bytes) [1048576]
--show-emwarns=no|yes show warnings about emulation limits? [no]
--require-text-symbol=:sonamepattern:symbolpattern abort run if the
stated shared object doesn't have the stated
text symbol. Patterns can contain ? and *.
--soname-synonyms=syn1=pattern1,syn2=pattern2,... synonym soname
specify patterns for function wrapping or replacement.
To use a non-libc malloc library that is
in the main exe: --soname-synonyms=somalloc=NONE
in libxyzzy.so: --soname-synonyms=somalloc=libxyzzy.so
--sigill-diagnostics=yes|no warn about illegal instructions? [yes]
--unw-stack-scan-thresh=<number> Enable stack-scan unwind if fewer
than <number> good frames found [0, meaning "disabled"]
NOTE: stack scanning is only available on arm-linux.
--unw-stack-scan-frames=<number> Max number of frames that can be
recovered by stack scanning [5]
--resync-filter=no|yes|verbose [yes on MacOS, no on other OSes]
attempt to avoid expensive address-space-resync operations
--max-threads=<number> maximum number of threads that valgrind can
handle [500]
user options for Memcheck:
--leak-check=no|summary|full search for memory leaks at exit? [summary]
--leak-resolution=low|med|high differentiation of leak stack traces [high]
--show-leak-kinds=kind1,kind2,.. which leak kinds to show?
[definite,possible]
--errors-for-leak-kinds=kind1,kind2,.. which leak kinds are errors?
[definite,possible]
where kind is one of:
definite indirect possible reachable all none
--leak-check-heuristics=heur1,heur2,... which heuristics to use for
improving leak search false positive [all]
where heur is one of:
stdstring length64 newarray multipleinheritance all none
--show-reachable=yes same as --show-leak-kinds=all
--show-reachable=no --show-possibly-lost=yes
same as --show-leak-kinds=definite,possible
--show-reachable=no --show-possibly-lost=no
same as --show-leak-kinds=definite
--xtree-leak=no|yes output leak result in xtree format? [no]
--xtree-leak-file=<file> xtree leak report file [xtleak.kcg.%p]
--undef-value-errors=no|yes check for undefined value errors [yes]
--track-origins=no|yes show origins of undefined values? [no]
--partial-loads-ok=no|yes too hard to explain here; see manual [yes]
--expensive-definedness-checks=no|auto|yes
Use extra-precise definedness tracking [auto]
--freelist-vol=<number> volume of freed blocks queue [20000000]
--freelist-big-blocks=<number> releases first blocks with size>= [1000000]
--workaround-gcc296-bugs=no|yes self explanatory [no]. Deprecated.
Use --ignore-range-below-sp instead.
--ignore-ranges=0xPP-0xQQ[,0xRR-0xSS] assume given addresses are OK
--ignore-range-below-sp=<number>-<number> do not report errors for
accesses at the given offsets below SP
--malloc-fill=<hexnumber> fill malloc'd areas with given value
--free-fill=<hexnumber> fill free'd areas with given value
--keep-stacktraces=alloc|free|alloc-and-free|alloc-then-free|none
stack trace(s) to keep for malloc'd/free'd areas [alloc-and-free]
--show-mismatched-frees=no|yes show frees that don't match the allocator? [yes]
Extra options read from ~/.valgrindrc, $VALGRIND_OPTS, ./.valgrindrc
Memcheck is Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
Valgrind is Copyright (C) 2000-2017, and GNU GPL'd, by Julian Seward et al.
LibVEX is Copyright (C) 2004-2017, and GNU GPL'd, by OpenWorks LLP et al.
Bug reports, feedback, admiration, abuse, etc, to: www.valgrind.org.
| オプション | 意味 |
|---|---|
| --show-reachable | yesを指定すると、無害かもしれないメモリリークも出力します |
| --trace-children | yesを指定すると、解析中のプログラムがforkっで生成したプロセスも解析対象にします |
| --track-fds | yesを指定すると、ファイルディスクリプタの閉じ忘れを報告します |
| --error-limit | noを指定すると、エラー数が閾値を超えた場合でも、Valgrindが解析を継続します |
| --num-callers | エラー箇所までのバックトレースを何段まで表示するかを指定します |
5 メモリリークを検出する方法
5.1 テストプログラムの作成
メモリリークが発生するテストプログラムを作成します。2秒毎にメモリ領域(10byte)を獲得しますが、解放はしません。
[root@server ~]# cat test.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int func()
{
char *p;
while(1) {
p = malloc(10);
sleep(2);
}
return 0;
}
int main(int argc, char *argv[])
{
pid_t pid;
int status;
pid = fork();
if(pid == 0){
func();
exit(EXIT_SUCCESS);
}
else if(pid > 0) {
wait(&status);
exit(EXIT_SUCCESS);
}
return 0;
}テストプログラムコンパイルします。コンパイルすると警告が出力されますが、宣言した変数を使用していないだけなので、無視してください。
[root@server ~]# gcc -Wall -g -o test test.c
作成したテストプログラムを確認します。
[root@server ~]# ls -l test* -rwxr-xr-x. 1 root root 10040 2月 17 08:35 test -rw-r--r--. 1 root root 448 2月 17 08:32 test.c
5.2 実行結果の確認
テストプログラムを引数に指定してvalgrindを実行します。
[root@server ~]# valgrind --leak-check=full --leak-resolution=high --show-reachable=yes ./test ==1771== Memcheck, a memory error detector ==1771== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==1771== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info ==1771== Command: ./test ==1771==
適当な時間(sleep関数で指定した2秒以上)が経過したら、Ctrl+Cを押下してvalgrindを終了します。メモリ解放漏れが発生している旨(*)が出力されています。
(*) definitely lost: 40 bytes in 4 blocks
^C==1772== ==1772== Process terminating with default action of signal 2 (SIGINT) ==1771== ==1771== Process terminating with default action of signal 2 (SIGINT) ==1771== at 0x4EFC3E2: wait (in /usr/lib64/libc-2.17.so) ==1772== at 0x4EFC840: __nanosleep_nocancel (in /usr/lib64/libc-2.17.so) ==1771== by 0x4006B1: main (test.c:29) ==1772== by 0x4EFC6F3: sleep (in /usr/lib64/libc-2.17.so) ==1772== by 0x40066C: func (test.c:13) ==1772== by 0x400695: main (test.c:25) ==1772== ==1772== HEAP SUMMARY: ==1772== in use at exit: 50 bytes in 5 blocks ==1772== total heap usage: 5 allocs, 0 frees, 50 bytes allocated ==1772== ==1771== ==1772== 10 bytes in 1 blocks are still reachable in loss record 1 of 2 ==1771== HEAP SUMMARY: ==1771== in use at exit: 0 bytes in 0 blocks ==1771== total heap usage: 0 allocs, 0 frees, 0 bytes allocated ==1771== ==1772== at 0x4C29F73: malloc (vg_replace_malloc.c:309) ==1771== All heap blocks were freed -- no leaks are possible ==1772== by 0x40065E: func (test.c:12) ==1771== ==1772== by 0x400695: main (test.c:25) ==1771== For lists of detected and suppressed errors, rerun with: -s ==1772== ==1771== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) ==1772== 40 bytes in 4 blocks are definitely lost in loss record 2 of 2 ==1772== at 0x4C29F73: malloc (vg_replace_malloc.c:309) ==1772== by 0x40065E: func (test.c:12) ==1772== by 0x400695: main (test.c:25) ==1772== ==1772== LEAK SUMMARY: ==1772== definitely lost: 40 bytes in 4 blocks ==1772== indirectly lost: 0 bytes in 0 blocks ==1772== possibly lost: 0 bytes in 0 blocks ==1772== still reachable: 10 bytes in 1 blocks ==1772== suppressed: 0 bytes in 0 blocks ==1772== ==1772== For lists of detected and suppressed errors, rerun with: -s ==1772== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
6 解放済みメモリー領域へのポインタを誤って使ってしまう問題の検出方法
6.1 テストプログラムの作成
解放したメモリ領域に書き込みをすると、メモリ破壊を引き起こします。このような問題はuse after free問題と言われています。以下のテストプログラムでは、解放した領域の先頭1バイトに1を書き込んでいます。
[root@server ~]# cat test.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int func()
{
char *p;
while(1) {
p = malloc(10);
free(p);
p[0] = 1; //解放した領域に書き込みを行う
sleep(2);
}
return 0;
}
int main(int argc, char *argv[])
{
pid_t pid;
int status;
pid = fork();
if(pid == 0){
func();
exit(EXIT_SUCCESS);
}
else if(pid > 0) {
wait(&status);
exit(EXIT_SUCCESS);
}
return 0;
}
6.2 実行結果の確認
テストプログラムを引数に指定してvalgrindを実行します。valgrindを実行すると、不正な書き込みをしたことを意味する"Invalid write of size 1が表示されます。
[root@server ~]# valgrind --leak-check=full --leak-resolution=high --show-reachable=yes ./test ==1796== Memcheck, a memory error detector ==1796== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==1796== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info ==1796== Command: ./test ==1796== ==1797== Invalid write of size 1 ==1797== at 0x4006C3: func (test.c:14) ==1797== by 0x4006F8: main (test.c:27) ==1797== Address 0x5205040 is 0 bytes inside a block of size 10 free'd ==1797== at 0x4C2B06D: free (vg_replace_malloc.c:540) ==1797== by 0x4006BE: func (test.c:13) ==1797== by 0x4006F8: main (test.c:27) ==1797== Block was alloc'd at ==1797== at 0x4C29F73: malloc (vg_replace_malloc.c:309) ==1797== by 0x4006AE: func (test.c:12) ==1797== by 0x4006F8: main (test.c:27) ==1797==
適当な時間(最低2秒以上)が経過したら、Ctrl+Cを押下してvalgrindを終了します。今回は、メモリの解放漏れがないので、"All heap blocks were freed -- no leaks are possible"が表示されています。
^C==1797== ==1797== Process terminating with default action of signal 2 (SIGINT) ==1796== ==1796== Process terminating with default action of signal 2 (SIGINT) ==1797== at 0x4EFC840: __nanosleep_nocancel (in /usr/lib64/libc-2.17.so) ==1797== by 0x4EFC6F3: sleep (in /usr/lib64/libc-2.17.so) ==1797== by 0x4006CF: func (test.c:15) ==1797== by 0x4006F8: main (test.c:27) ==1796== at 0x4EFC3E2: wait (in /usr/lib64/libc-2.17.so) ==1796== by 0x400714: main (test.c:31) ==1796== ==1796== HEAP SUMMARY: ==1796== in use at exit: 0 bytes in 0 blocks ==1796== total heap usage: 0 allocs, 0 frees, 0 bytes allocated ==1796== ==1796== All heap blocks were freed -- no leaks are possible ==1796== ==1796== For lists of detected and suppressed errors, rerun with: -s ==1796== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) ==1797== ==1797== HEAP SUMMARY: ==1797== in use at exit: 0 bytes in 0 blocks ==1797== total heap usage: 5 allocs, 5 frees, 50 bytes allocated ==1797== ==1797== All heap blocks were freed -- no leaks are possible ==1797== ==1797== For lists of detected and suppressed errors, rerun with: -s ==1797== ERROR SUMMARY: 5 errors from 1 contexts (suppressed: 0 from 0)
7 確保したメモリ領域外への不正アクセスの検出方法
7.1 テストプログラムの作成
確保したメモリ領域外への書き込みをすると、隣接するメモリ領域を破壊してしまいます。ここでは、このような問題の検出方法を説明します。
[root@server ~]# cat test.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int func()
{
char *p;
while(1) {
p = malloc(10);
p[10] = 1; //確保した領域外への書き込みを行う。
free(p);
sleep(2);
}
return 0;
}
int main(int argc, char *argv[])
{
pid_t pid;
int status;
pid = fork();
if(pid == 0){
func();
exit(EXIT_SUCCESS);
}
else if(pid > 0) {
wait(&status);
exit(EXIT_SUCCESS);
}
return 0;
}
7.2 実行結果の確認
テストプログラムを引数に指定してvalgrindを実行します。valgrindを実行すると、不正な書き込みをしたことを意味する"Invalid write of size 1が表示されます。
[root@server ~]# valgrind --leak-check=full --leak-resolution=high --show-reachable=yes ./test ==2099== Memcheck, a memory error detector ==2099== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==2099== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info ==2099== Command: ./test ==2099== ==2100== Invalid write of size 1 ==2100== at 0x4006BB: func (test.c:13) ==2100== by 0x4006FC: main (test.c:28) ==2100== Address 0x520504a is 0 bytes after a block of size 10 alloc'd ==2100== at 0x4C29F73: malloc (vg_replace_malloc.c:309) ==2100== by 0x4006AE: func (test.c:12) ==2100== by 0x4006FC: main (test.c:28) ==2100== || 適当な時間(最低2秒以上)が経過したら、Ctrl+Cを押下してvalgrindを終了します。メモリの解放漏れがないので、"All heap blocks were freed -- no leaks are possible"が表示されています。 >|| ^C==2099== ==2099== Process terminating with default action of signal 2 (SIGINT) ==2099== at 0x4EFC3E2: wait (in /usr/lib64/libc-2.17.so) ==2099== by 0x400718: main (test.c:32) ==2100== ==2100== Process terminating with default action of signal 2 (SIGINT) ==2100== at 0x4EFC840: __nanosleep_nocancel (in /usr/lib64/libc-2.17.so) ==2100== by 0x4EFC6F3: sleep (in /usr/lib64/libc-2.17.so) ==2100== by 0x4006D3: func (test.c:15) ==2100== by 0x4006FC: main (test.c:28) ==2100== ==2100== HEAP SUMMARY: ==2100== in use at exit: 0 bytes in 0 blocks ==2100== total heap usage: 9 allocs, 9 frees, 90 bytes allocated ==2100== ==2100== All heap blocks were freed -- no leaks are possible ==2100== ==2100== For lists of detected and suppressed errors, rerun with: -s ==2100== ERROR SUMMARY: 9 errors from 1 contexts (suppressed: 0 from 0) ==2099== ==2099== HEAP SUMMARY: ==2099== in use at exit: 0 bytes in 0 blocks ==2099== total heap usage: 0 allocs, 0 frees, 0 bytes allocated ==2099== ==2099== All heap blocks were freed -- no leaks are possible ==2099== ==2099== For lists of detected and suppressed errors, rerun with: -s ==2099== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Z 参考図書
私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ

