C/C++的内存管理一直都是程序员最头痛的事情,内存越界、数组越界、内存泄漏、内存溢出、野指针、空指针...,随便一个问题都可能让程序崩溃。而且往往问题的源头都比较隐蔽,让人很难排查出问题的根源所在。
想要解决这个问题,还得从问题的根源入手。valgrind是一个强大的内存管理工具,常用来检测内存泄漏和内存的非法使用,用好了可以很好的从根源上解决c/c++内存管理的问题
1 Valgrind在PC机上的使用
1.1 Valgrind在PC上的安装
下载
wget https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2
解压
tarxjvfvalgrind-3.15.0.tar.bz2
cd valgrind-3.15.0
配置
./configure
编译
make
安装
make install
查看版本
valgrind–version
输出如下信息说明安装成功
valgrind-3.15.0
1.2 使用Valgrind检测内存
先准备一个测试文件memtest.c
该程序有两处错误:第18行发生了越界错误,test2()没有翻译指针p。
编译程序,加上-g可以准确报错出错的行数。
gccmemtest.c -g -o memtest
使用valgrind|检查内存
valgrind --tool=memcheck --leak-check=full ./memtest
输出:
==29683== Memcheck, a memory error detector
==29683== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==29683== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==29683== Command: ./memtest
==29683==
test memory
test free ...
==29683== Invalid write of size 2
==29683== at 0x108759: test (memtest.c:18)
==29683== by 0x10879D: main (memtest.c:26)
==29683== Address 0x522f504 is 4 bytes inside a block of size 5 alloc'd
==29683== at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==29683== by 0x10874A: test (memtest.c:17)
==29683== by 0x10879D: main (memtest.c:26)
==29683==
malloc test in test2
exit ......
==29683==
==29683== HEAP SUMMARY:
==29683== in use at exit: 20 bytes in 1 blocks
==29683== total heap usage: 4 allocs, 3 frees, 1,113 bytes allocated
==29683==
==29683== 20 bytes in 1 blocks are definitely lost in loss record 1 of 1
==29683== at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==29683== by 0x1086E3: test2 (memtest.c:7)
==29683== by 0x108774: test (memtest.c:21)
==29683== by 0x10879D: main (memtest.c:26)
==29683==
==29683== LEAK SUMMARY:
==29683== definitely lost: 20 bytes in 1 blocks
==29683== indirectly lost: 0 bytes in 0 blocks
==29683== possibly lost: 0 bytes in 0 blocks
==29683== still reachable: 0 bytes in 0 blocks
==29683== suppressed: 0 bytes in 0 blocks
==29683==
==29683== For counts of detected and suppressed errors, rerun with: -v
==29683== ERROR SUMMARY: 3 errors from 2 contexts (suppressed: 0 from 0)
Valgrind检查到一处非法写入内存Invalid write of size 2,出现在代码at 0x108759: test (memtest.c:18)。strcpy(ps,"ABCDE");拷贝了五个字符,再加一个’/0’,发生越界。
LEAK SUMMARY显示Valring检查到一处内存泄露,泄露了20个字节。
test2()分配了20个字节的内存,但是没有释放该内存。
修复这两个问题,再次编译运行。
摘要表明,程序分配了4次内存,释放了4次,所有的堆空间都被释放,没有内存泄露。
1.3 使用Valgrind检测持续内存泄露
准备一个测试文件mem_run.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void *thread_function(void *arg);
char message[] = "Hello World";
int main() {
int res;
pthread_ta_thread;
void *thread_result;
res = pthread_create(&a_thread, NULL, thread_function, (void *)message);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
printf("Waiting for thread to finish...\n");
res = pthread_join(a_thread, &thread_result);
if (res != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf("Thread joined, res %d, it returned %s\n", res, (char *)thread_result);
printf("Message is now %s\n", message);
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
char *p = NULL;
inti = 0;
while(1){
p = (char *)malloc(sizeof(char) * 10);
strcpy(p,"1234");
i ++;
printf("i = %d\n", i);
sleep(1);
}
}
thread_function持续地分配内存,每次分配10个字节,但不释放。
编译运行
gcc mem_run.c -g -o mem_run -lpthread
valgrind --tool=memcheck --leak-check=full --log-file=./valgrind_report.log ./mem_run
这次将log输出到文件valgrind_report.log中。
运行6秒钟后,按ctrl+c终止程序,查看valgrind_report.log。
摘要表明6处共60个字节的内存泄露了。
2 Valgrind在嵌入式系统EU项目上的使用
2.1编译安装
下载
wget https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2
解压
tarxjvfvalgrind-3.15.0.tar.bz2
进入valgrind目录
cd valgrind-3.15.0
配置
修改configure: armv7*) 改成 armv7*|arm)
./configure --prefix=/home/yangkemeng/valgrind_install --host=arm-linux \
CC=/home/yangkemeng/eu/ti_components/os_tools/linux/linaro/gcc-linaro-5.3-2016.02-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc \
CXX=/home/yangkemeng/eu/ti_components/os_tools/linux/linaro/gcc-linaro-5.3-2016.02-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-g++
--prefix指定安装目录,--host 指定芯片架构,CC和CXX指定交叉编译器路径,不可使用相对路径。
输出如下表明配置成功。
编译
Make
安装
Make install
安装后valgrind_install目录下多了5个目录bin 、 include、 lib libexec、 share
2.2 Valgrind移植到EU开发板
将valgrind_install目录拷贝到开发板的/opt/vision_sdk/目录,
给valgrind_install目录加上可执行权限
chmod-R +x valgrind_install
编辑/etc/profile添加下面两行:
export VALGRIND_LIB=/opt/vision_sdk/valgrind_install/lib/valgrind
export PATH=/opt/vision_sdk/valgrind_install/bin:$PATH
执行如下命令,使环境变量生效
Source /etc/profile
查看valgrind版本,执行valgrind–version,若是输出valgrind-3.15.0,说明移植成功。
2.3 Eu开发板子上测试Valgrind
使用交叉编译器编译之前的memtest.c文件,将生成的memtest拷贝到开发板
../eu/ti_components/os_tools/linux/linaro/gcc-linaro-5.3-2016.02-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc -g memtest.c -o memtest
添加可执行权限
chmod 777 memtest
使用Valgrind检查内存问题
valgrind --tool=memcheck --leak-check=full --log-file=./valgrind_report.log ./memtest
3 小结
Valgrind是检查内存问题的有力工具,在项目中使用和推广可以让代码的稳定性可靠性大大提高。