C++单元测试之三--内存泄漏问题检查

简介: 在程序的性能指标中,内存是一个很重要的方面。内存问题包括很多方面:内存泄漏,非法指针使用(包括很常见的问题--使用未初始化指针),数组越界,栈溢出和奇地址访问等。这些问题在C/C++语言当中尤为明显,因为很少有其他语言比如java提供指针直接访问地址和内存空间。
在程序的性能指标中,内存是一个很重要的方面。内存问题包括很多方面:内存泄漏,非法指针使用(包括很常见的问题--使用未初始化指针),数组越界,栈溢出和奇地址访问等。这些问题在C/C++语言当中尤为明显,因为很少有其他语言比如java提供指针直接访问地址和内存空间。
而内存问题越早发现,越好解决,给产品带来的负面影响也越小。今天我就给大家介绍一个很简单的在单元测试阶段发现内存问题的方法。这就是使用valgrind(在linux平台),使用MALLOCDEBUG在AIX平台或使用Purify在Solaris平台。
修改Makefile如下(Linux平台):

点击(此处)折叠或打开

  1. OBJS= Main.o A.o

  2. UTest.out: $(OBJS)
  3. g++ -o UTest.out $(OBJS) -lgcov

  4. CURDIR=$(shell pwd)

  5. $(OBJS): %.o: %.cpp
  6. g++ -I$(CURDIR) -c -g -fprofile-arcs -ftest-coverage $

  7. #unit test and code coverage check
  8. UT:UTest.out
  9. valgrind --leak-check=full ./UTest.out
  10. ./gcov_checker 90 --objects $(OBJS)

  11. .PHONY: clean
  12. clean:
  13. rm -f *.o *.gcno *.gcda *.gcov UTest.out
修改A.cpp使其有内存泄漏:

点击(此处)折叠或打开

  1. #include "A.h"
  2. #include stdio.h>


  3. int A::Sum(int a, int b)
  4. {
  5.     return a+b;
  6. }

  7. int A::Multiply(int a, int b)
  8. {
  9.         char *p = new char[16];
  10.     return a*b;
  11. }


编译运行结果是:

点击(此处)折叠或打开

  1. [root@tivu25 utcov]# make clean;make UT
  2. rm -f *.o *.gcno *.gcda *.gcov UTest.out
  3. g++ -I/home/haoqf/src/UTest/utcov -c -g -fprofile-arcs -ftest-coverage Main.cpp -o Main.o
  4. g++ -I/home/haoqf/src/UTest/utcov -c -g -fprofile-arcs -ftest-coverage A.cpp -o A.o
  5. g++ -o UTest.out Main.o A.o -lgcov
  6. valgrind --leak-check=full ./UTest.out
  7. ==5578== Memcheck, a memory error detector.
  8. ==5578== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al.
  9. ==5578== Using LibVEX rev 1658, a library for dynamic binary translation.
  10. ==5578== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP.
  11. ==5578== Using valgrind-3.2.1, a dynamic binary instrumentation framework.
  12. ==5578== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al.
  13. ==5578== For more details, rerun with: -v
  14. ==5578==
  15. Sum test passed!
  16. Multiply test passed!
  17. ==5578==
  18. ==5578== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 15 from 1)
  19. ==5578== malloc/free: in use at exit: 16 bytes in 1 blocks.
  20. ==5578== malloc/free: 3 allocs, 2 frees, 720 bytes allocated.
  21. ==5578== For counts of detected errors, rerun with: -v
  22. ==5578== searching for pointers to 1 not-freed blocks.
  23. ==5578== checked 92,988 bytes.
  24. ==5578==
  25. ==5578== 16 bytes in 1 blocks are definitely lost in loss record 1 of 1
  26. ==5578== at 0x40057F5: operator new[](unsigned) (vg_replace_malloc.c:195)
  27. ==5578== by 0x8048C39: A::Multiply(int, int) (A.cpp:12)
  28. ==5578== by 0x8048B23: main (Main.cpp:14)
  29. ==5578==
  30. ==5578== LEAK SUMMARY:
  31. ==5578== definitely lost: 16 bytes in 1 blocks.
  32. ==5578== possibly lost: 0 bytes in 0 blocks.
  33. ==5578== still reachable: 0 bytes in 0 blocks.
  34. ==5578== suppressed: 0 bytes in 0 blocks.
  35. ==5578== Reachable blocks (those to which a pointer was found) are not shown.
  36. ==5578== To see them, rerun with: --show-reachable=yes
  37. ./gcov_checker 90 --objects Main.o A.o
  38. Code coverage:
  39. Main.cpp:66.67% - WARNING: code coverage
  40. A.cpp:100.0%
  41. See *.gcov files for details.
可以看出valgrind确实检测出了内存泄漏位于A::Multiply函数中,且在A.cpp的第12行。

不仅如此,valgrind还可以检测出其他很多内存问题,比如数组越界,非法地址访问等。
比如我们修改A.cpp如下:


点击(此处)折叠或打开

  1. #include "A.h"
  2. #include stdio.h>
  3. #include string.h>


  4. int A::Sum(int a, int b)
  5. {

  6.     return a+b;
  7. }

  8. int A::Multiply(int a, int b)
  9. {
  10.     char *p1;
  11.     strcpy(p1, "hello");

  12.     return a*b;
  13. }

编译运行结果是:

点击(此处)折叠或打开

  1. [root@tivu25 utcov]# make clean;make UT
  2. rm -f *.o *.gcno *.gcda *.gcov UTest.out
  3. g++ -I/home/haoqf/src/UTest/utcov -c -g -fprofile-arcs -ftest-coverage Main.cpp -o Main.o
  4. g++ -I/home/haoqf/src/UTest/utcov -c -g -fprofile-arcs -ftest-coverage A.cpp -o A.o
  5. g++ -o UTest.out Main.o A.o -lgcov
  6. valgrind --leak-check=full ./UTest.out
  7. ==5742== Memcheck, a memory error detector.
  8. ==5742== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al.
  9. ==5742== Using LibVEX rev 1658, a library for dynamic binary translation.
  10. ==5742== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP.
  11. ==5742== Using valgrind-3.2.1, a dynamic binary instrumentation framework.
  12. ==5742== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al.
  13. ==5742== For more details, rerun with: -v
  14. ==5742==
  15. Sum test passed!
  16. ==5742== Use of uninitialised value of size 4
  17. ==5742== at 0x8048BE1: A::Multiply(int, int) (A.cpp:20)
  18. ==5742== by 0x8048AD3: main (Main.cpp:14)
  19. ==5742==
  20. ==5742== Invalid write of size 4
  21. ==5742== at 0x8048BE1: A::Multiply(int, int) (A.cpp:20)
  22. ==5742== by 0x8048AD3: main (Main.cpp:14)
  23. ==5742== Address 0x0 is not stack'd, malloc'd or (recently) free'd
  24. ==5742==
  25. ==5742== Process terminating with default action of signal 11 (SIGSEGV)
  26. ==5742== Access not within mapped region at address 0x0
  27. ==5742== at 0x8048BE1: A::Multiply(int, int) (A.cpp:20)
  28. ==5742== by 0x8048AD3: main (Main.cpp:14)
  29. ==5742==
  30. ==5742== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 15 from 1)
  31. ==5742== malloc/free: in use at exit: 0 bytes in 0 blocks.
  32. ==5742== malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
  33. ==5742== For counts of detected errors, rerun with: -v
  34. ==5742== All heap blocks were freed -- no leaks are possible.
  35. make: *** [UT] Segmentation fault


再比如数组越界:

点击(此处)折叠或打开

  1. #include "A.h"
  2. #include stdio.h>
  3. #include string.h>


  4. int A::Sum(int a, int b)
  5. {

  6.     return a+b;
  7. }

  8. int A::Multiply(int a, int b)
  9. {
  10.     
  11.         char *array = new char[100];
  12.         for(int i = 0; i 101; i++)
  13.                 array[i] = i;
  14.     delete [] array;




  15.     return a*b;
  16. }


运行结果是:

点击(此处)折叠或打开

  1. [root@tivu25 utcov]# make clean;make UT
  2. rm -f *.o *.gcno *.gcda *.gcov UTest.out
  3. g++ -I/home/haoqf/src/UTest/utcov -c -g -fprofile-arcs -ftest-coverage Main.cpp -o Main.o
  4. g++ -I/home/haoqf/src/UTest/utcov -c -g -fprofile-arcs -ftest-coverage A.cpp -o A.o
  5. g++ -o UTest.out Main.o A.o -lgcov
  6. valgrind --tool=memcheck --leak-check=full ./UTest.out
  7. ==5876== Memcheck, a memory error detector.
  8. ==5876== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al.
  9. ==5876== Using LibVEX rev 1658, a library for dynamic binary translation.
  10. ==5876== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP.
  11. ==5876== Using valgrind-3.2.1, a dynamic binary instrumentation framework.
  12. ==5876== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al.
  13. ==5876== For more details, rerun with: -v
  14. ==5876==
  15. Sum test passed!
  16. ==5876== Invalid write of size 1
  17. ==5876== at 0x8048C9D: A::Multiply(int, int) (A.cpp:21)
  18. ==5876== by 0x8048B53: main (Main.cpp:14)
  19. ==5876== Address 0x401908C is 0 bytes after a block of size 100 alloc'd
  20. ==5876== at 0x40057F5: operator new[](unsigned) (vg_replace_malloc.c:195)
  21. ==5876== by 0x8048C69: A::Multiply(int, int) (A.cpp:19)
  22. ==5876== by 0x8048B53: main (Main.cpp:14)
  23. Multiply test
  24. ==5876==
  25. ==5876== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 15 from 1)
  26. ==5876== malloc/free: in use at exit: 0 bytes in 0 blocks.
  27. ==5876== malloc/free: 3 allocs, 3 frees, 804 bytes allocated.
  28. ==5876== For counts of detected errors, rerun with: -v
  29. ==5876== All heap blocks were freed -- no leaks are possible.
  30. ./gcov_checker 90 --objects Main.o A.o
  31. Code coverage:
  32. Main.cpp:66.67% - WARNING: code coverage 90.00%
  33. A.cpp:100.0%
  34. See *.gcov files for details.


《返璞归真--UNIX技术内幕》在全国各大书店及网城均有销售:

京东
亚马逊                          China pub
上学吧                          1号店





目录
相关文章
|
27天前
|
C++
【C++】深入解析C/C++内存管理:new与delete的使用及原理(二)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
27天前
|
编译器 C++ 开发者
【C++】深入解析C/C++内存管理:new与delete的使用及原理(三)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
27天前
|
存储 C语言 C++
【C++】深入解析C/C++内存管理:new与delete的使用及原理(一)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
2月前
|
程序员 编译器 C++
【C++核心】C++内存分区模型分析
这篇文章详细解释了C++程序执行时内存的四个区域:代码区、全局区、栈区和堆区,以及如何在这些区域中分配和释放内存。
47 2
|
22天前
|
存储 程序员 编译器
简述 C、C++程序编译的内存分配情况
在C和C++程序编译过程中,内存被划分为几个区域进行分配:代码区存储常量和执行指令;全局/静态变量区存放全局变量及静态变量;栈区管理函数参数、局部变量等;堆区则用于动态分配内存,由程序员控制释放,共同支撑着程序运行时的数据存储与处理需求。
75 21
|
10天前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
14天前
|
存储 C语言 C++
【C++打怪之路Lv6】-- 内存管理
【C++打怪之路Lv6】-- 内存管理
32 0
【C++打怪之路Lv6】-- 内存管理
|
2月前
|
缓存 Java 测试技术
谷粒商城笔记+踩坑(11)——性能压测和调优,JMeter压力测试+jvisualvm监控性能+资源动静分离+修改堆内存
使用JMeter对项目各个接口进行压力测试,并对前端进行动静分离优化,优化三级分类查询接口的性能
谷粒商城笔记+踩坑(11)——性能压测和调优,JMeter压力测试+jvisualvm监控性能+资源动静分离+修改堆内存
|
23天前
|
存储 C语言 C++
【C/C++内存管理】——我与C++的不解之缘(六)
【C/C++内存管理】——我与C++的不解之缘(六)
|
26天前
|
程序员 C语言 C++
C++入门5——C/C++动态内存管理(new与delete)
C++入门5——C/C++动态内存管理(new与delete)
53 1