在程序的性能指标中,内存是一个很重要的方面。内存问题包括很多方面:内存泄漏,非法指针使用(包括很常见的问题--使用未初始化指针),数组越界,栈溢出和奇地址访问等。这些问题在C/C++语言当中尤为明显,因为很少有其他语言比如java提供指针直接访问地址和内存空间。
而内存问题越早发现,越好解决,给产品带来的负面影响也越小。今天我就给大家介绍一个很简单的在单元测试阶段发现内存问题的方法。这就是使用valgrind(在linux平台),使用MALLOCDEBUG在AIX平台或使用Purify在Solaris平台。
修改Makefile如下(Linux平台):
- OBJS= Main.o A.o
- UTest.out: $(OBJS)
- g++ -o UTest.out $(OBJS) -lgcov
- CURDIR=$(shell pwd)
- $(OBJS): %.o: %.cpp
- g++ -I$(CURDIR) -c -g -fprofile-arcs -ftest-coverage $< -o $@
- #unit test and code coverage check
- UT:UTest.out
- valgrind --leak-check=full ./UTest.out
- ./gcov_checker 90 --objects $(OBJS)
- .PHONY: clean
- clean:
- rm -f *.o *.gcno *.gcda *.gcov UTest.out
修改A.cpp使其有内存泄漏:- #include "A.h"
- #include <stdio.h>
- int A::Sum(int a, int b)
- {
- return a+b;
- }
- int A::Multiply(int a, int b)
- {
- char *p = new char[16];
- return a*b;
- }
编译运行结果是:- [root@tivu25 utcov]# make clean;make UT
- rm -f *.o *.gcno *.gcda *.gcov UTest.out
- g++ -I/home/haoqf/src/UTest/utcov -c -g -fprofile-arcs -ftest-coverage Main.cpp -o Main.o
- g++ -I/home/haoqf/src/UTest/utcov -c -g -fprofile-arcs -ftest-coverage A.cpp -o A.o
- g++ -o UTest.out Main.o A.o -lgcov
- valgrind --leak-check=full ./UTest.out
- ==5578== Memcheck, a memory error detector.
- ==5578== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al.
- ==5578== Using LibVEX rev 1658, a library for dynamic binary translation.
- ==5578== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP.
- ==5578== Using valgrind-3.2.1, a dynamic binary instrumentation framework.
- ==5578== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al.
- ==5578== For more details, rerun with: -v
- ==5578==
- Sum test passed!
- Multiply test passed!
- ==5578==
- ==5578== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 15 from 1)
- ==5578== malloc/free: in use at exit: 16 bytes in 1 blocks.
- ==5578== malloc/free: 3 allocs, 2 frees, 720 bytes allocated.
- ==5578== For counts of detected errors, rerun with: -v
- ==5578== searching for pointers to 1 not-freed blocks.
- ==5578== checked 92,988 bytes.
- ==5578==
- ==5578== 16 bytes in 1 blocks are definitely lost in loss record 1 of 1
- ==5578== at 0x40057F5: operator new[](unsigned) (vg_replace_malloc.c:195)
- ==5578== by 0x8048C39: A::Multiply(int, int) (A.cpp:12)
- ==5578== by 0x8048B23: main (Main.cpp:14)
- ==5578==
- ==5578== LEAK SUMMARY:
- ==5578== definitely lost: 16 bytes in 1 blocks.
- ==5578== possibly lost: 0 bytes in 0 blocks.
- ==5578== still reachable: 0 bytes in 0 blocks.
- ==5578== suppressed: 0 bytes in 0 blocks.
- ==5578== Reachable blocks (those to which a pointer was found) are not shown.
- ==5578== To see them, rerun with: --show-reachable=yes
- ./gcov_checker 90 --objects Main.o A.o
- Code coverage:
- Main.cpp:66.67% - WARNING: code coverage < 90.00%
- A.cpp:100.0%
- See *.gcov files for details.
可以看出valgrind确实检测出了内存泄漏位于A::Multiply函数中,且在A.cpp的第12行。
不仅如此,valgrind还可以检测出其他很多内存问题,比如数组越界,非法地址访问等。
比如我们修改A.cpp如下:- #include "A.h"
- #include <stdio.h>
- #include <string.h>
- int A::Sum(int a, int b)
- {
- return a+b;
- }
- int A::Multiply(int a, int b)
- {
- char *p1;
- strcpy(p1, "hello");
- return a*b;
- }
编译运行结果是:- [root@tivu25 utcov]# make clean;make UT
- rm -f *.o *.gcno *.gcda *.gcov UTest.out
- g++ -I/home/haoqf/src/UTest/utcov -c -g -fprofile-arcs -ftest-coverage Main.cpp -o Main.o
- g++ -I/home/haoqf/src/UTest/utcov -c -g -fprofile-arcs -ftest-coverage A.cpp -o A.o
- g++ -o UTest.out Main.o A.o -lgcov
- valgrind --leak-check=full ./UTest.out
- ==5742== Memcheck, a memory error detector.
- ==5742== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al.
- ==5742== Using LibVEX rev 1658, a library for dynamic binary translation.
- ==5742== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP.
- ==5742== Using valgrind-3.2.1, a dynamic binary instrumentation framework.
- ==5742== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al.
- ==5742== For more details, rerun with: -v
- ==5742==
- Sum test passed!
- ==5742== Use of uninitialised value of size 4
- ==5742== at 0x8048BE1: A::Multiply(int, int) (A.cpp:20)
- ==5742== by 0x8048AD3: main (Main.cpp:14)
- ==5742==
- ==5742== Invalid write of size 4
- ==5742== at 0x8048BE1: A::Multiply(int, int) (A.cpp:20)
- ==5742== by 0x8048AD3: main (Main.cpp:14)
- ==5742== Address 0x0 is not stack'd, malloc'd or (recently) free'd
- ==5742==
- ==5742== Process terminating with default action of signal 11 (SIGSEGV)
- ==5742== Access not within mapped region at address 0x0
- ==5742== at 0x8048BE1: A::Multiply(int, int) (A.cpp:20)
- ==5742== by 0x8048AD3: main (Main.cpp:14)
- ==5742==
- ==5742== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 15 from 1)
- ==5742== malloc/free: in use at exit: 0 bytes in 0 blocks.
- ==5742== malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
- ==5742== For counts of detected errors, rerun with: -v
- ==5742== All heap blocks were freed -- no leaks are possible.
- make: *** [UT] Segmentation fault
再比如数组越界:- #include "A.h"
- #include <stdio.h>
- #include <string.h>
- int A::Sum(int a, int b)
- {
- return a+b;
- }
- int A::Multiply(int a, int b)
- {
-
- char *array = new char[100];
- for(int i = 0; i < 101; i++)
- array[i] = i;
- delete [] array;
- return a*b;
- }
运行结果是:- [root@tivu25 utcov]# make clean;make UT
- rm -f *.o *.gcno *.gcda *.gcov UTest.out
- g++ -I/home/haoqf/src/UTest/utcov -c -g -fprofile-arcs -ftest-coverage Main.cpp -o Main.o
- g++ -I/home/haoqf/src/UTest/utcov -c -g -fprofile-arcs -ftest-coverage A.cpp -o A.o
- g++ -o UTest.out Main.o A.o -lgcov
- valgrind --tool=memcheck --leak-check=full ./UTest.out
- ==5876== Memcheck, a memory error detector.
- ==5876== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al.
- ==5876== Using LibVEX rev 1658, a library for dynamic binary translation.
- ==5876== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP.
- ==5876== Using valgrind-3.2.1, a dynamic binary instrumentation framework.
- ==5876== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al.
- ==5876== For more details, rerun with: -v
- ==5876==
- Sum test passed!
- ==5876== Invalid write of size 1
- ==5876== at 0x8048C9D: A::Multiply(int, int) (A.cpp:21)
- ==5876== by 0x8048B53: main (Main.cpp:14)
- ==5876== Address 0x401908C is 0 bytes after a block of size 100 alloc'd
- ==5876== at 0x40057F5: operator new[](unsigned) (vg_replace_malloc.c:195)
- ==5876== by 0x8048C69: A::Multiply(int, int) (A.cpp:19)
- ==5876== by 0x8048B53: main (Main.cpp:14)
- Multiply test
- ==5876==
- ==5876== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 15 from 1)
- ==5876== malloc/free: in use at exit: 0 bytes in 0 blocks.
- ==5876== malloc/free: 3 allocs, 3 frees, 804 bytes allocated.
- ==5876== For counts of detected errors, rerun with: -v
- ==5876== All heap blocks were freed -- no leaks are possible.
- ./gcov_checker 90 --objects Main.o A.o
- Code coverage:
- Main.cpp:66.67% - WARNING: code coverage < 90.00%
- A.cpp:100.0%
- See *.gcov files for details.
《返璞归真--UNIX技术内幕》在全国各大书店及网城均有销售:
阅读(1923) | 评论(0) | 转发(0) |