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号店





目录
相关文章
|
1月前
|
监控 Java
压力测试Jmeter的简单使用,性能监控-堆内存与垃圾回收 -jvisualvm的使用
这篇文章介绍了如何使用JMeter进行压力测试,包括测试前的配置、测试执行和结果查看。同时,还探讨了性能监控工具jconsole和jvisualvm的使用,特别是jvisualvm,它可以监控内存泄露、跟踪垃圾回收、执行时内存和CPU分析以及线程分析等,文章还提供了使用这些工具的详细步骤和说明。
压力测试Jmeter的简单使用,性能监控-堆内存与垃圾回收 -jvisualvm的使用
|
1月前
|
存储 编译器 C语言
【C++】C\C++内存管理
【C++】C\C++内存管理
【C++】C\C++内存管理
|
6天前
|
缓存 Java 测试技术
谷粒商城笔记+踩坑(11)——性能压测和调优,JMeter压力测试+jvisualvm监控性能+资源动静分离+修改堆内存
使用JMeter对项目各个接口进行压力测试,并对前端进行动静分离优化,优化三级分类查询接口的性能
谷粒商城笔记+踩坑(11)——性能压测和调优,JMeter压力测试+jvisualvm监控性能+资源动静分离+修改堆内存
|
8天前
|
Java 测试技术 Android开发
Android性能测试——发现和定位内存泄露和卡顿
本文详细介绍了Android应用性能测试中的内存泄漏与卡顿问题及其解决方案。首先,文章描述了使用MAT工具定位内存泄漏的具体步骤,并通过实例展示了如何分析Histogram图表和Dominator Tree。接着,针对卡顿问题,文章探讨了其产生原因,并提供了多种测试方法,包括GPU呈现模式分析、FPS Meter软件测试、绘制圆点计数法及Android Studio自带的GPU监控功能。最后,文章给出了排查卡顿问题的四个方向,帮助开发者优化应用性能。
36 4
Android性能测试——发现和定位内存泄露和卡顿
|
1月前
|
编译器 C++
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决
|
16天前
|
C语言 C++
C++(二)内存管理
本文档详细介绍了C++中的内存管理机制,特别是`new`和`delete`关键字的使用方法。首先通过示例代码展示了如何使用`new`和`delete`进行单个变量和数组的内存分配与释放。接着讨论了内存申请失败时的处理方式,包括直接抛出异常、使用`try/catch`捕获异常、设置`set_new_handler`函数以及不抛出异常的处理方式。通过这些方法,可以有效避免内存泄漏和多重释放的问题。
|
1月前
|
存储 Java C语言
【C++】C/C++内存管理
【C++】C/C++内存管理
|
1月前
|
存储 编译器 C语言
C++内存管理(区别C语言)深度对比
C++内存管理(区别C语言)深度对比
60 5
|
30天前
|
缓存 Ubuntu Linux
在Linux中,如何检查系统的CPU和内存使用情况?
在Linux中,如何检查系统的CPU和内存使用情况?
|
1月前
|
存储 程序员 编译器
c++学习笔记08 内存分区、new和delete的用法
C++内存管理的学习笔记08,介绍了内存分区的概念,包括代码区、全局区、堆区和栈区,以及如何在堆区使用`new`和`delete`进行内存分配和释放。
38 0