diff --git a/test/testsuites/duma/leak1.c b/test/testsuites/duma/leak1.c new file mode 100644 index 000000000..fdbc4a653 --- /dev/null +++ b/test/testsuites/duma/leak1.c @@ -0,0 +1,12 @@ +#include + +int main() { + printf("Hello world!\n"); + + int* pI; + pI = (int*)malloc(sizeof(int)); + printf("Let's leak a pointer to int\n"); + *pI = 303; + + return 0; +} diff --git a/test/testsuites/duma/leak1.cc b/test/testsuites/duma/leak1.cc new file mode 100644 index 000000000..6f97dbdd9 --- /dev/null +++ b/test/testsuites/duma/leak1.cc @@ -0,0 +1,15 @@ +#include +#include +#include + +using namespace std; + +int main() { + cout << "Hello world!" << endl; + + int* pI = new int; + cerr << "Let's leak a pointer to int" << endl; + *pI = 303; + + return 0; +} diff --git a/test/testsuites/duma/leak2.c b/test/testsuites/duma/leak2.c new file mode 100644 index 000000000..923754d49 --- /dev/null +++ b/test/testsuites/duma/leak2.c @@ -0,0 +1,20 @@ +#include + +int main() { + printf("Hello world!\n"); + + int* pI; + pI = (int*)malloc(10*sizeof(int)); + + printf("Let's leak a pointer to an array of 10 ints\n"); + int i=0; + for (i=0; i<9; i++) { + pI[i] = 303+i; + } + int j=0; + for (j=0; j<9; j++) { + if (pI[j] != 303+j) printf(" Something strange is happening...\n"); + } + + return 0; +} diff --git a/test/testsuites/duma/leak2.cc b/test/testsuites/duma/leak2.cc new file mode 100644 index 000000000..e71db91d5 --- /dev/null +++ b/test/testsuites/duma/leak2.cc @@ -0,0 +1,20 @@ +#include +#include +#include + +using namespace std; + +int main() { + cout << "Hello world!" << endl; + + int* pI = new int[10]; + cerr << "Let's leak a pointer to an array of 10 ints" << endl; + for (int i=0; i<9; i++) { + pI[i] = 303+i; + } + for (int i=0; i<9; i++) { + if (pI[i] != 303+i) cerr << " Something strange is happening..." << endl; + } + + return 0; +} diff --git a/test/testsuites/duma/memCheckers.html b/test/testsuites/duma/memCheckers.html new file mode 100644 index 000000000..123b729f8 --- /dev/null +++ b/test/testsuites/duma/memCheckers.html @@ -0,0 +1,367 @@ + +Comparison of Free Memory Checkers + + + + + + + + + +

Jean-Philippe Martin | +Resources | Memory Checkers Comparison

+
+

+ +

Memory Checkers

+ +Memory checkers are debugging tools that help programmers find +improper use of pointers, typically memory leaks. + +

+There are some freely available memory checkers. I ran a series of +very simple tests to determine what they can do. The wrong +series of tests contains code that makes pointer mistakes that are not +memory leaks, for example freeing a pointer twice, writing to +uninitialized memory or using delete instead of delete []. The +leak series of tests contains simple memory leaks, +i.e. pointers that are allocated but not released. The ok +series of tests contains programs that are correct and thus should not +cause the memory checker to output any alarm message. +

+ +

C tests

+ +(updated 3/10/2006)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Checker + + wrong1.c + wrong3.c + wrong6.c + wrong7.c + leak1.c + leak2.c + ok5.c + +
MALLOC_CHECK_ + + OK + missed + missed + OK* + missed + missed + OK +
dmalloc + OK + missed + missed + OK + OK + OK + OK +
memCheckDeluxe + + missed + missed + missed + missed + OK + OK + OK +
memwatch + + OK + missed + missed* + OK + OK + OK + OK +
DUMA + OK + + missed + missed + + OK + OK + OK* + OK +
valgrind + + OK + OK + missed + OK + OK + OK + OK +
+ +

+ +

C++ tests

+ + + + + + + + + + + + + + + + + + + +
Checker + + wrong1.cc + wrong2.cc + wrong3.cc + wrong4.cc + wrong5.cc + wrong6.cc + leak1.cc + leak2.cc + ok5.cc + +
MALLOC_CHECK_ + + OK + OK + missed + missed + OK + missed + missed + missed + OK +
dmalloc + OK + OK* + missed* + missed + OK + missed + OK + OK + missed* +
DUMA + OK + OK + + missed + OK + OK + missed + OK + OK + missed +
valgrind + + OK + OK + OK* + OK + OK + missed + OK + OK + OK +
+

+ +

Conclusion

+ +

+memWatch and memCheckDeluxe are both memory leak detectors, and they +passed all the memory leak tests. Memwatch wins this round because it +was able to detect the double-free in wrong1.c and the out-of-bounds +accesses in the dynamically allocated array of wrong7.c (not the +static array of wrong6 - but no one else did, either). +Both programs are designed to work with C and require a +recompilation. +

+ +

+MALLOC_CHECK_ is an interesting test: it is triggered simply by +setting the environment variable MALLOC_CHECK_ to 1, and the rest of +the magic is done by glibc (see the link in references, below). This +is the easiest check to set up and it requires no recompilation. It +detected the double free in wrong1 and the mismatched malloc/delete or +new/free pairs in wrong2.cc and wrong5.cc. It was able to see that +something was fishy in wrong7.c, but it reports a single error at the "free" +instead of when we are accessing the memory instead of two errors, for +each out-of-bounds access. MALLOC_CHECK_ cannot detect +memory leaks and did not detect the use of uninitialized memory in +wrong3. +

+ +

+dmalloc is more than a leak detector, but it didn't detect as +many bad cases as valgrind and requires a recompile. Also, its C++ +support is (in the author's words) minimal. In particular, I have not +been able to get dmalloc to report line numbers with C++ (log), although that feature mostly +works with C code - in both leak1.c and leak2.c it pointed to +the return() instead of the line that allocated the unfreed +memory. Dmalloc also often reports unfreed memory, even for programs +that are correct. This may be because of errors in the c++ library, +but it makes the reports harder to read. In contrast, valgrind has a +way to hide leaks that it knows about so its reports are more +clear. See also the author's comments. +

+ +

+valgrind is clearly the winner of this little contest. valgrind +requires no recompilation of the program, so it's very easy to set +up. It identified almost all of the incorrect pointer uses and memory +leaks. The only test that it missed is wrong6, in which we break the +bounds of an array. No other checker spotted that one, though. Also, +valgrind has been improved since we ran this test, so it may perform +even better than what we show here. +

+ +

+DUMA is a very close second. The results I am posting here come from +Koneru Srikanth (kpsrikanth at gmail dot com) who generously sent them +to me. DUMA seems not to require a recompile, but the tests were run +on recompiled code. DUMA performs really well. It was also able to +detect out-of-bounds writes +(it is reported as failing wrong3.cc because it missed the +out-of-bounds read). If for some reason valgrind does not work for +you, then I recommend that you give DUMA a spin. + + +

Reference

+ +I tested: + +
    +
  • MALLOC_CHECK_ + for glibc (C and C++: requires no recompilation) +
  • dmalloc-5.2.2 (C, minimal C++ support; requires recompilation) +
  • memCheckDeluxe-1.2.2 (C, +some C++. Requires recompilation) +
  • memwatch-2.71 (C +only; requires recompilation) +
  • valgrind-1.9.6 + (C, C++ and more: requires no recompilation) +
  • DUMA version 2.4.26 + (C and C++. Documentation says that no recompilation is needed, but + the tests were run on recompiled code) (as mentioned above, these tests + were contributed by Koneru Srikanth). +
+ +I did not test: + + + +Test programs: + + + +

ToDo

+ +The following memory checkers have been mentionned to me but I haven't +tried them yet: +
    +
  • mpatrol at http://www.cbmamiga.demon.co.uk/mpatrol/ +
+ +

Change History

+ +March 10, 2006: added DUMA, contributed by Koneru Srikanth +
+Oct 6, 2003: mention of mpatrol +
+Sept 29, 2003: added dmalloc +
+June 25, 2003: minor change in the text +
+June 24, 2003: corrected result for memwatch's wrong1.c, added wrong7.c +
+June 15, 2003: initial release +

+ +Please contact me if you have feedback or +would like to suggest another tool for the test. + +


+ [JP Martin] + [resources] + [contact information] +

+

+Best viewed with *any* browser +
+ + \ No newline at end of file diff --git a/test/testsuites/duma/wrong1.c b/test/testsuites/duma/wrong1.c new file mode 100644 index 000000000..bcbbd2113 --- /dev/null +++ b/test/testsuites/duma/wrong1.c @@ -0,0 +1,13 @@ +#include + +int main() { + printf("Hello world!\n"); + + int* pI = (int*)malloc(sizeof(int)); + *pI=2; + free(pI); + printf("Now freeing a pointer twice...\n"); + free(pI); + printf("Did you notice?\n"); + return 0; +} diff --git a/test/testsuites/duma/wrong1.cc b/test/testsuites/duma/wrong1.cc new file mode 100644 index 000000000..ecb9c6085 --- /dev/null +++ b/test/testsuites/duma/wrong1.cc @@ -0,0 +1,16 @@ +#include +#include +#include + +using namespace std; + +int main() { + cout << "Hello world!" << endl; + int* pI = new int; + *pI=2; + delete(pI); + cerr << "Now deleting a pointer twice..." << endl; + delete(pI); + cerr << "Did you notice?" << endl; + return 0; +} diff --git a/test/testsuites/duma/wrong2.cc b/test/testsuites/duma/wrong2.cc new file mode 100644 index 000000000..2c7786a29 --- /dev/null +++ b/test/testsuites/duma/wrong2.cc @@ -0,0 +1,25 @@ +#include +#include +#include + +using namespace std; + +int main() { + cout << "Hello world!" << endl; + + int* pI = new int; + *pI=2; + cerr << "Now freeing a pointer instead of deleting it..." << endl; + free(pI); + cerr << "Did you notice?" << endl; + + + pI = new int; + delete(pI); + cerr << "Now deleting twice..." << endl; + delete(pI); + cerr << "Did you notice?" << endl; + + cerr << "There should be 2 errors in this run" << endl; + return 0; +} diff --git a/test/testsuites/duma/wrong3.c b/test/testsuites/duma/wrong3.c new file mode 100644 index 000000000..80b4c3998 --- /dev/null +++ b/test/testsuites/duma/wrong3.c @@ -0,0 +1,22 @@ +#include + +int main() { + printf("Hello world!\n"); + + int* pI = (int*)malloc(sizeof(int)); + int j; + printf("Now reading uninitialized memory\n"); + j = *pI+2; + printf("Did you notice? (value was %i)\n",j); + free(pI); + printf("(No memory leak here)\n"); + + int* pJ; + printf("Now writing to uninitialized pointer\n"); + *pJ = j; + printf("Did you notice?\n"); + + // valgrind reports 8, but that's ok + printf("There should be 2 errors in this run\n"); + return 0; +} diff --git a/test/testsuites/duma/wrong3.cc b/test/testsuites/duma/wrong3.cc new file mode 100644 index 000000000..309b38e0a --- /dev/null +++ b/test/testsuites/duma/wrong3.cc @@ -0,0 +1,26 @@ +#include +#include +#include + +using namespace std; + +int main() { + cout << "Hello world!" << endl; + + int* pI = new int; + int j; + cerr << "Now reading uninitialized memory" << endl; + j = *pI+2; + cerr << "Did you notice? (value was " << j << ") " << endl; + delete pI; + cerr << "(No memory leak here)" << endl; + + int* pJ; + cerr << "Now writing to uninitialized pointer" << endl; + *pJ = j; + cerr << "Did you notice?" << endl; + + // valgrind reports 4, but that's ok + cerr << "There should be 2 errors in this run" << endl; + return 0; +} diff --git a/test/testsuites/duma/wrong4.cc b/test/testsuites/duma/wrong4.cc new file mode 100644 index 000000000..2e83c9b52 --- /dev/null +++ b/test/testsuites/duma/wrong4.cc @@ -0,0 +1,26 @@ +#include +#include +#include + +using namespace std; + +int main() { + cout << "Hello world!" << endl; + + { + int* pI = new int[10]; + cerr << "Let's delete instead of delete [] " << endl; + delete pI; + cerr << "Did you notice?" << endl; + } + + { + int* pI = new int[10]; + cerr << "Now let's free instead of delete [] " << endl; + free(pI); + cerr << "Did you notice?" << endl; + } + + cerr << "There should be 2 errors in this run" << endl; + return 0; +} diff --git a/test/testsuites/duma/wrong5.cc b/test/testsuites/duma/wrong5.cc new file mode 100644 index 000000000..50c6b6885 --- /dev/null +++ b/test/testsuites/duma/wrong5.cc @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +using namespace std; + +class Test { +public: + int a; + string stdstr; + + Test() { + a=2; + stdstr = "test"; + } + +}; + +int main() { + cout << "Hello world!" << endl; + + { + Test* pI = new Test[10]; + cerr << "Let's delete instead of delete [] " << endl; + delete pI; + cerr << "Did you notice?" << endl; + } + + { + Test* pI = new Test[10]; + cerr << "Now let's free instead of delete [] " << endl; + free(pI); + cerr << "Did you notice?" << endl; + } + + cerr << "There should be 2 errors in this run" << endl; + return 0; +} diff --git a/test/testsuites/duma/wrong6.c b/test/testsuites/duma/wrong6.c new file mode 100644 index 000000000..973e89b9d --- /dev/null +++ b/test/testsuites/duma/wrong6.c @@ -0,0 +1,19 @@ +#include + +struct Test { + int a; + char st[10]; +}; + +int main() { + printf("Hello world!\n"); + + struct Test ar[10]; + struct Test b; + printf("Let's index out of bounds \n"); + ar[10].a=10; + printf("Did you notice?\n"); + + printf("There should be 1 error in this run\n"); + return 0; +} diff --git a/test/testsuites/duma/wrong6.cc b/test/testsuites/duma/wrong6.cc new file mode 100644 index 000000000..85dd1ffef --- /dev/null +++ b/test/testsuites/duma/wrong6.cc @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +using namespace std; + +class Test { +public: + int a; + string stdstr; + + Test() { + a=2; + stdstr = "test"; + } + + void doNothing() { + cout << " hi!" << endl; + }; + +}; + +int main() { + cout << "Hello world!" << endl; + + Test ar[10]; + Test b; + cerr << "Let's index out of bounds " << endl; + ar[10].doNothing(); + cerr << "Did you notice?" << endl; + + cerr << "There should be 1 error in this run" << endl; + return 0; +} diff --git a/test/testsuites/duma/wrong7.c b/test/testsuites/duma/wrong7.c new file mode 100644 index 000000000..0faf6913a --- /dev/null +++ b/test/testsuites/duma/wrong7.c @@ -0,0 +1,13 @@ +#include + +int main() { + int *p; + + p = (int*) malloc( sizeof(int) * 10 ); + printf("Now writing before our allocated array\n"); + p[-1] ^= 0x0F; /* bash before */ + printf("... and now after our allocated array\n"); + p[10] ^= 0x0F; /* bash after */ + printf("Did you notice?\n"); + free(p); +}