valgrind使用

简介

Valgrind是构建动态分析工具的框架,Valgrind工具可以自动检测许多内存管理和线程错误,并详细介绍您的程序,还可以使用Valgrind构建新工具。

官网:http://valgrind.org/
valgrind的使用:http://valgrind.org/docs/manual/manual.html
下载地址:git clone git://sourceware.org/git/valgrind.git


交叉编译

查看README.aarch64README.android文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Building
~~~~~~~~

You could probably build it directly on a target OS, using the normal
non-cross scheme

./autogen.sh ; ./configure --prefix=.. ; make ; make install

Development so far was however done by cross compiling, viz:

export CC=aarch64-linux-gnu-gcc
export LD=aarch64-linux-gnu-ld
export AR=aarch64-linux-gnu-ar

./autogen.sh
./configure --prefix=`pwd`/Inst --host=aarch64-unknown-linux \
--enable-only64bit
make -j4
make -j4 install

Doing this assumes that the install path (`pwd`/Inst) is valid on
both host and target, which isn't normally the case. To avoid
this limitation, do instead:

./configure --prefix=/install/path/on/target \
--host=aarch64-unknown-linux \
--enable-only64bit
make -j4
make -j4 install DESTDIR=/a/temp/dir/on/host
# and then copy the contents of DESTDIR to the target.

See README.android for more examples of cross-compile building.

参照上面的做法,开始交叉编译:

1
2
3
4
5
6
7
8
9
10
mkdir out
export CC=~/disk2/work2/imx8x/toolchains/aarch64-imx8x-linux/bin/aarch64-poky-linux-gcc
export LD=~/disk2/work2/imx8x/toolchains/aarch64-imx8x-linux/bin/aarch64-poky-linux-ld
export AR=~/disk2/work2/imx8x/toolchains/aarch64-imx8x-linux/bin/aarch64-poky-linux-ar
./autogen.sh
./configure --prefix=/home/victor/disk2/work2/util/valgrind/out/ \
--host=aarch64-poky-linux \
--enable-only64bit
make -j4
make -j4 install

编译生成的文件放在./out目录下:

1
2
3
4
5
6
7
victor@victor-HP:~/disk2/work2/util/valgrind# ll out/
total 20
drwxrwxr-x 5 victor victor 4096 3月 20 16:27 .
drwxrwxr-x 29 victor victor 4096 3月 20 16:27 ..
drwxrwxr-x 2 victor victor 4096 3月 20 16:27 bin
drwxrwxr-x 3 victor victor 4096 3月 20 16:27 include
drwxrwxr-x 4 victor victor 4096 3月 20 16:27 lib


拷贝到目标板子上

1、拷贝方法一:
将编译出来的out/目录整个拷贝到目标板子上,比如说拷贝到目标板子的/var/目录下,然后设置环境变量export VALGRIND_LIB="/out/bin/lib/valgrind",设置完毕后就可以进入到cd /var/bin/bin目录下开始执行valgrind命令。
如果这里不执行设置环境变量,运行valgrind会出现如下错误:

1
valgrind: failed to start tool 'memcheck' for platform 'arm-linux': No such file or directory

2、拷贝方法二:
将生成的 bin/include/lib/三个文件的内容放到目标板子上对应的位置,就可以直接执行valgrind命令了。


运行

在板子上执行如下命令测试应用程序:
./valgrind --leak-check=full /var/test_app,正常情况下,就能够开始检查并输出结果,但是我的这边执行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Fatal error at startup: a function redirection
which is mandatory for this platform-tool combination
cannot be set up. Details of the redirection are:


valgrind: Fatal error at startup: a function redirection
valgrind: which is mandatory for this platform-tool combination
valgrind: cannot be set up. Details of the redirection are:
valgrind:
valgrind: A must-be-redirected function
valgrind: whose name matches the pattern: strlen
valgrind: in an object with soname matching: ld-linux-aarch64.so.1
valgrind: was not found whilst processing
valgrind: symbols from the object with soname: ld-linux-aarch64.so.1
valgrind:
valgrind: Possible fixes: (1, short term): install glibc's debuginfo
valgrind: package on this machine. (2, longer term): ask the packagers
valgrind: for your Linux distribution to please in future ship a non-
valgrind: stripped ld.so (or whatever the dynamic linker .so is called)
valgrind: that exports the above-named function using the standard
valgrind: calling conventions for this platform. The package you need
valgrind: to install for fix (1) is called
valgrind:
valgrind: On Debian, Ubuntu: libc6-dbg
valgrind: On SuSE, openSuSE, Fedora, RHEL: glibc-debuginfo
valgrind:
valgrind: Note that if you are debugging a 32 bit process on a
valgrind: 64 bit system, you will need a corresponding 32 bit debuginfo
valgrind: package (e.g. libc6-dbg:i386).
valgrind:
valgrind: Cannot continue -- exiting now. Sorry.

按照上面的提示,就是说ld-linux-aarch64.so.1文件不带debuginfo的,也就是stripped的,调试信息已经被剥离,所以valgrind无法运行。果然,找到板子上的ld-linux-aarch64.so.1执行file命令,看到的结果是stripped

1
2
3
4
# file ld-linux-aarch64.so.1
ld-linux-aarch64.so.1: symbolic link to ld-2.27.so
# file ld-2.27.so
ld-2.27.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, stripped

关于strippednot-stripped的区别在:
https://unix.stackexchange.com/questions/2969/what-are-stripped-and-not-stripped-executables-in-unix

1
2
3
4
5
6
7
If you compile an executable with gcc's -g flag, it contains debugging information. 
That means for each instruction there is information which line of the source code generated it,
the name of the variables in the source code is retained
and can be associated to the matching memory at runtime etc.
Strip can remove this debugging information
and other data included in the executable which is not necessary for execution
in order to reduce the size of the executable.


解决方法一

按照提示,我们需要使用到not-strippedld-linux-aarch64.so.1文件,刚好看到yocto上有编译出valgrind,lib文件放在tmp/work/aarch64-poky-linux/valgrind/3.13.0-r0/recipe-sysroot/lib目录下,查看到ld-linux-aarch64.so.1的文件类型是not stripped类型,符合我们的要求。

1
2
3
4
# file ld-linux-aarch64.so.1
ld-linux-aarch64.so.1: symbolic link to ld-2.27.so
# file ld-2.27.so
ld-2.27.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, not stripped

解决方法就是将其拷贝到目标板子上,当然,这里不单单是要拷贝ld-linux-aarch64.so.1文件,也要拷贝libc.so.6libc-2.27.so文件。
当然,这个只是临时的测试方法,最最根据的解决办法还是在系统中编译出带not stripped的文件。


解决方法二

因为yocto有现成编译好的valgrind,所以我们之间将tmp/work/aarch64-poky-linux/valgrind/3.13.0-r0/image/生成的可执行文件和库之间拷贝到目标板子上,参照上面的拷贝方法,此方法可以顺利的运行,不需要用到not-stripped类型的ld-linux-aarch64.so.1文件。
这里遗留了一个问题,就是yocto编译的valgrind和我们自己交叉编译出来的valgrindld-linux-aarch64.so.1文件strippednot stripped的依赖程度不一样。yocto不需要not stripped的,而我们自己交叉编译出来的需要。


再次运行

我们准备了一个测试程序test_app,如下,这个程序有malloc但是没有free,保证会内存泄露。

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
printf("[xxx] 111\n");
int *p = malloc(50);
printf("[xxx] 222\n");
return 0;
}

使用valgrind运行检查test_app程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@android:/var/out/bin # ./valgrind --leak-check=full /var/test_app
==1661== Memcheck, a memory error detector
==1661== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==1661== Using Valgrind-3.15.0.GIT and LibVEX; rerun with -h for copyright info
==1661== Command: /var/test_app
==1661==
[xxx] 111
[xxx] 222
==1661==
==1661== HEAP SUMMARY:
==1661== in use at exit: 50 bytes in 1 blocks
==1661== total heap usage: 2 allocs, 1 frees, 4,146 bytes allocated
==1661==
==1661== 50 bytes in 1 blocks are definitely lost in loss record 1 of 1
==1661== at 0x4845100: malloc (vg_replace_malloc.c:309)
==1661== by 0x40060F: main (test_app.c:7)
==1661==
==1661== LEAK SUMMARY:
==1661== definitely lost: 50 bytes in 1 blocks
==1661== indirectly lost: 0 bytes in 0 blocks
==1661== possibly lost: 0 bytes in 0 blocks
==1661== still reachable: 0 bytes in 0 blocks
==1661== suppressed: 0 bytes in 0 blocks
==1661==
==1661== For lists of detected and suppressed errors, rerun with: -s
==1661== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

从上面可以很清楚的看出,出现了50个字节的内存泄露,泄露位置在test_app.c的第7行。其中0x40060F为堆栈信息,可以通过objdump反汇编出来,比如说:aarch64-poky-linux-objdump -d -l -f -g -S test_app > test_app_objdump.txt,或者使用addr2line查看代码所在位置aarch64-poky-linux-addr2line -e test_app 0x40060F。如果addr2line出来的结果是??:0,可以去解析带not strippedtest_app的二进制文件,通常放在out/target/xxx/yyy/symbol/文件下。


可能出现的错误

如果在运行valgrind一直刷新如下log,可以使用带--undef-value-errors=no的参数进行过滤。

1
==56903== Use of uninitialised value of size 8


参考资料:

内存、性能问题分析的利器——valgraind
内存问题分析的利器——valgraind的memcheck
valgrind 工具介绍和简单的使用