I am currently in the process of recreating a basic linux shell for my class and I'm having problems with valgrind detecting a memory leak even without the use of the new operator. I'm not sure where to look at and I can't make anything out of the valgrind error message. If anyone has any idea where the memory leak is coming from, that would be very appreciated. Thank you!
==26432==
==26432== HEAP SUMMARY:
==26432== in use at exit: 61 bytes in 2 blocks
==26432== total heap usage: 268 allocs, 266 frees, 28,546 bytes allocated
==26432==
==26432== 29 bytes in 1 blocks are possibly lost in loss record 1 of 2
==26432== at 0x4C2B0E0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==26432== by 0x4EF1208: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==26432== by 0x4EF1DCA: std::string::_Rep::_M_clone(std::allocator<char> const&, unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==26432== by 0x4EF1E63: std::string::reserve(unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==26432== by 0x4EACF5E: std::basic_istream<char, std::char_traits<char> >& std::getline<char, std::char_traits<char>, std::allocator<char> >(std::basic_istream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, char) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==26432== by 0x401895: main (in /home/randyhe/Documents/FALL2015/CS100/rshell/bin/rshell)
==26432==
==26432== 32 bytes in 1 blocks are possibly lost in loss record 2 of 2
==26432== at 0x4C2B0E0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==26432== by 0x4EF1208: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==26432== by 0x4EF2930: char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==26432== by 0x4EF2D47: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==26432== by 0x401826: main (in /home/randyhe/Documents/FALL2015/CS100/rshell/bin/rshell)
==26432==
==26432== LEAK SUMMARY:
==26432== definitely lost: 0 bytes in 0 blocks
==26432== indirectly lost: 0 bytes in 0 blocks
==26432== possibly lost: 61 bytes in 2 blocks
==26432== still reachable: 0 bytes in 0 blocks
==26432== suppressed: 0 bytes in 0 blocks
==26432==
==26432== For counts of detected and suppressed errors, rerun with: -v
A portion of my code is below.
#include <iostream>
#include <vector>
#include <queue>
#include <utility>
#include <sstream>
#include <string>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/wait.h>
#include <errno.h>
#include <limits.h>
#include <pwd.h>
std::pair<std::vector<std::vector<std::string> >, std::queue<std::string> >
parseInput(std::string s, bool& b);
void check(std::string singleWord, std::string keyword, bool& b, int& index,
std::vector<std::vector<std::string> >& v, std::queue<std::string>& q);
void executeAll(std::pair<std::vector<std::vector<std::string> >,
std::queue<std::string> > p);
void printCommands(const std::vector<std::vector<std::string> >& v);
void printConnectors(std::queue<std::string> q);
bool executeSingle(std::vector<std::string>& v);
std::vector<char*> toCharPointers(std::vector<std::string>& v);
char* getHost();
int main()
{
// Store hostname into character array object hostname
char hostname[HOST_NAME_MAX];
int status = gethostname(hostname, HOST_NAME_MAX);
if(status != 0)
{
perror("gethostname failed");
exit(errno);
}
// Store username to login object
struct passwd *pws;
pws = getpwuid(geteuid());
if(pws == NULL)
{
perror("getid failed");
exit(errno);
}
std::string login = pws->pw_name;
for(;;)
{
// Output login and hostname
std::cout << login << "@" << hostname << "$ ";
// Get user input
std::string userInput;
std::getline(std::cin, userInput);
// If user inputs exit, end program
if(userInput == "exit")
exit(0);
// parsedPair holds the list of commands on its .first, and holds
// the list of connectors on its .second
bool correctSyntax = true;
std::pair<std::vector<std::vector<std::string> >,
std::queue<std::string> > parsedPair =
parseInput(userInput, correctSyntax);
if(correctSyntax)
executeAll(parsedPair);
}
return 0;
}
If you know how to read it, you will see exactly what is going on:
==26432== HEAP SUMMARY:
==26432== in use at exit: 61 bytes in 2 blocks
==26432== total heap usage: 268 allocs, 266 frees, 28,546 bytes allocated
==26432==
==26432== 29 bytes in 1 blocks are possibly lost in loss record 1 of 2
==26432== at 0x4C2B0E0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==26432== by 0x4EF1208: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==26432== by 0x4EF1DCA: std::string::_Rep::_M_clone(std::allocator<char> const&, unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==26432== by 0x4EF1E63: std::string::reserve(unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==26432== by 0x4EACF5E: std::basic_istream<char, std::char_traits<char> >& std::getline<char, std::char_traits<char>, std::allocator<char> >(std::basic_istream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, char) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==26432== by 0x401895: main (in /home/randyhe/Documents/FALL2015/CS100/rshell/bin/rshell)
==26432==
==26432== 32 bytes in 1 blocks are possibly lost in loss record 2 of 2
==26432== at 0x4C2B0E0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==26432== by 0x4EF1208: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==26432== by 0x4EF2930: char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==26432== by 0x4EF2D47: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==26432== by 0x401826: main (in /home/randyhe/Documents/FALL2015/CS100/rshell/bin/rshell)
==26432==
This:
==26432== by 0x4EACF5E: std::basic_istream<char, std::char_traits<char> >& std::getline<char, std::char_traits<char>, std::allocator<char> >(std::basic_istream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, char) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
and this:
==26432== by 0x4EF2D47: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
are the constructors for strings (which calls new).
As the comment says, don't call exit(0); to get out of your loop, either use break or return 0 - that way the destructors for local objects at the end of main will be called, and the memory for your string variables released.
Bear in mind that exit is not something the C++ compiler recognizes with respect to what happens to objects. It's just a giant goto that exits your program. It does do SOME cleanup, like closing files, but does not clean up any objects. The same applies to setjmp/longjmp pairs - C++ will not clean up for those.
If you want to exit from a function that is called from main, one solution would be to throw an exception that is caught in main.
Of course, if you are exiting the program, a few memory leaks of this kind isn't terrible - it's a balance between cleaning up and making the code more complex. In this particular case, it's very easy to fix, so I would fix it. But if you have to make lots and lots of changes just to chase down a memory leak of a couple of objects that last the entire duration of the program, then it's not a huge issue (it's NICE to fix them, but not essential). The BAD memory leaks are the ones that leak something for every iteration of a loop or every call of a commonly called function - that's when your program will eventually own all the memory it can, and cause a problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With