mtrace is a nice GNU extension which is used to diagnose dynamic memory allocation in C/C++ programs. It can also be used for finding memory leaks. I am going to demonstrate a memory leak problem in a simple C++ program. Look at the following code:
// File: m-test.cpp
#include <iostream>
#include <cstdlib>
#define SIZE 16
using namespace std;
bool isPalindrome(char *);
int main()
{
char *str;
while(true)
{
str = (char *)malloc(SIZE);
cout << "Enter string to check Palindrome or quit to exit: ";
cin >> str;
if(strcmp(str, "quit") == 0)
break;
if(isPalindrome(str))
cout << str << " is a Palindrome." << endl;
else
cout << str << " is NOT a Palindrome." << endl;
}
return 0;
}
bool isPalindrome(char *str)
{
int str_len = strlen(str);
for(int i = 0; i < str_len/2; i++)
if(str[i] != str[str_len-i-1])
return false;
return true;
}
It is a simple C++ program which checks whether the input string is a palindrome or not. I compiled and ran using following commands and it exeucutes perfectly:
munir@ubuntu:~/prog/c$ g++ m-test.cpp -Wall -g -o m-test
munir@ubuntu:~/prog/c$ ./m-test
Enter string to check Palindrome or quit to exit: ababa
ababa is a Palindrome.
Enter string to check Palindrome or quit to exit: abb
abb is NOT a Palindrome.
Enter string to check Palindrome or quit to exit: quit
munir@ubuntu:~/prog/c$
But did you find any thing wrong by reading code?
There is something wrong with dynamic memory allocation by using malloc(). Note that malloc(SIZE) is called every time when the program takes input from command promt. Lets trace the memory allocation using mtrace by following easy steps:
1) Include mcheck.h header file.
2) Place mtrace() in the first line of main(). mtrace will start tracing memory from this point.
3) Place muntrace() in the end of main() to stop tracing.
4) At command promt type export MALLOC_TRACE=mtrace.log to save output in mtrace.log file.
5) Compile and run the program again.
It should execute exactly like before but this time it will create a new file mtrace.log. Now come to the fun part. Open the text from mtrace.log:
munir@ubuntu:~/prog/c$ ./m-test2
Enter string to check Palindrome or quit to exit: aba
aba is a Palindrome.
Enter string to check Palindrome or quit to exit: a
a is a Palindrome.
Enter string to check Palindrome or quit to exit: aa
aa is a Palindrome.
Enter string to check Palindrome or quit to exit: abb
abb is NOT a Palindrome.
Enter string to check Palindrome or quit to exit: anm
anm is NOT a Palindrome.
Enter string to check Palindrome or quit to exit: quit
munir@ubuntu:~/prog/c$ cat mtrace.log
= Start
@ ./m-test2:(__gxx_personality_v0+0x161)[0x80488e5] + 0x804a378 0x10
@ ./m-test2:(__gxx_personality_v0+0x161)[0x80488e5] + 0x804a390 0x10
@ ./m-test2:(__gxx_personality_v0+0x161)[0x80488e5] + 0x804a3a8 0x10
@ ./m-test2:(__gxx_personality_v0+0x161)[0x80488e5] + 0x804a3c0 0x10
@ ./m-test2:(__gxx_personality_v0+0x161)[0x80488e5] + 0x804a3d8 0x10
@ ./m-test2:(__gxx_personality_v0+0x161)[0x80488e5] + 0x804a3f0 0x10
= End
munir@ubuntu:~/prog/c$
This is not in very readable form but what I understood from this result is that my program was allocated 0x10 bytes(16 bytes in decimal) of memory 6 times because malloc() was called 6 times.
Since the content of the log file is not very clear to understand therefore I am going to use a useful Perl script to interpret the results. This script is also called mtrace and its syntax is very simple to use: mtrace program log-file
munir@ubuntu:~/prog/c$ mtrace m-test2 mtrace.log
Memory not freed:
-----------------
Address Size Caller
0x0804a378 0x10 at /home/munir/prog/c/m-test2.cpp:17
0x0804a390 0x10 at /home/munir/prog/c/m-test2.cpp:17
0x0804a3a8 0x10 at /home/munir/prog/c/m-test2.cpp:17
0x0804a3c0 0x10 at /home/munir/prog/c/m-test2.cpp:17
0x0804a3d8 0x10 at /home/munir/prog/c/m-test2.cpp:17
0x0804a3f0 0x10 at /home/munir/prog/c/m-test2.cpp:17
munir@ubuntu:~/prog/c$
This indicates that there is a potential memory leak in program at line 17 which is caused by dynamic memory allocation by calling malloc() and the program does not free the allocated memory. free() must be used in order to solve the problem. Here is the final version with no such memory leak:
// File: m-test3.cpp
#include <iostream>
#include <cstdlib>
#define SIZE 16
using namespace std;
bool isPalindrome(char *);
int main()
{
char *str;
while(true)
{
str = (char *)malloc(SIZE);
cout << "Enter string to check Palindrome or quit to exit: ";
cin >> str;
if(strcmp(str, "quit") == 0)
{
free(str);
break;
}
if(isPalindrome(str))
cout << str << " is a Palindrome." << endl;
else
cout << str << " is NOT a Palindrome." << endl;
free(str);
}
return 0;
}
bool isPalindrome(char *str)
{
int str_len = strlen(str);
for(int i = 0; i < str_len/2; i++)
if(str[i] != str[str_len-i-1])
return false;
return true;
}
And running the test again:
munir@ubuntu:~/prog/c$ g++ m-test3.cpp -Wall -g -o m-test3
munir@ubuntu:~/prog/c$ ./m-test3
Enter string to check Palindrome or quit to exit: a
a is a Palindrome.
Enter string to check Palindrome or quit to exit: aaa
aaa is a Palindrome.
Enter string to check Palindrome or quit to exit: abc
abc is NOT a Palindrome.
Enter string to check Palindrome or quit to exit: aBbabBa
aBbabBa is a Palindrome.
Enter string to check Palindrome or quit to exit: aBbabBA
aBbabBA is NOT a Palindrome.
Enter string to check Palindrome or quit to exit: quit
munir@ubuntu:~/prog/c$ cat mtrace.log
= Start
@ ./m-test3:(__gxx_personality_v0+0x171)[0x8048915] + 0x804a378 0x10
@ ./m-test3:(__gxx_personality_v0+0x244)[0x80489e8] - 0x804a378
@ ./m-test3:(__gxx_personality_v0+0x171)[0x8048915] + 0x804a378 0x10
@ ./m-test3:(__gxx_personality_v0+0x244)[0x80489e8] - 0x804a378
@ ./m-test3:(__gxx_personality_v0+0x171)[0x8048915] + 0x804a378 0x10
@ ./m-test3:(__gxx_personality_v0+0x244)[0x80489e8] - 0x804a378
@ ./m-test3:(__gxx_personality_v0+0x171)[0x8048915] + 0x804a378 0x10
@ ./m-test3:(__gxx_personality_v0+0x244)[0x80489e8] - 0x804a378
@ ./m-test3:(__gxx_personality_v0+0x171)[0x8048915] + 0x804a378 0x10
@ ./m-test3:(__gxx_personality_v0+0x244)[0x80489e8] - 0x804a378
@ ./m-test3:(__gxx_personality_v0+0x171)[0x8048915] + 0x804a378 0x10
@ ./m-test3:(__gxx_personality_v0+0x1bd)[0x8048961] - 0x804a378
= End
munir@ubuntu:~/prog/c$ mtrace m-test3 mtrace.log
No memory leaks.
munir@ubuntu:~/prog/c$
So we just saw how easy is to detect memory leak using mtrace utility.
Source codes: m-test.cpp m-test2.cpp m-test3.cpp
Screen shot: here