Follow this blog by Email

Wednesday, 18 September 2013

Memory leaks, errors... plug that

Memory leak is the phenomenon when a dynamically allocated memory using malloc or new operator(C++) is not freed and returned to the language runtime/OS. This is a big nemesis in large programs which allocate a new block of  memory recurrently, maybe every time it is called , use more than one pointers to such memory and those pointers are passed as arguments to functions but that memory  freeing up does not happen at proper locations of the code at proper times, which causes a memory block to exist, but the pointer handle to refer that is lost.

Languages like C,C++ inherently(provided by the language specification) do not manage the automatic freeing up of the memory, so programmers have to free all such dynamically allocated memory themselves.
 Such memory leaks cause the usage of the memory in a system to grow if the process is left to run for long time(over night or over days depending upon how large/small and how quickly/slowly the memory is leaked..) and they are a programmers nemesis if they are present in the code.
There are many tools to help in finding if and where(which function/file) there is , and there could be a memory leak.

Valgrind is one of the tool(valgrind is really a set of tools ) which assists in finding memory leaks on a linux platform. Memcheck is the tool in the valgrind suite which helps here. It can analyse C, C++ , Java code and report definite memory leaks, possible leaks or such memory errors like accessing an array out of its allocated bounds, using uninitialized variable etc.

Below is a commandline I use most often to find run a memory leak check analysis on my code on linux:
valgrind --tool=memcheck --leak-check=full -v --track-origins=yes  --leak-check=full  --track-origins-yes  --log-file=valgrind.log --show-reachable=yes ./executable optionstoexecutable

Executing valgrind memcheck on my code, has saved me a lot of headache and debugging hours by assisting in finding run-time memory related issues in the code.

The generated valgrind.log file can have some of the below information. These is what I got from running valgrind , on one of my projects.

==2807== 1,347,584 bytes in 1,316 blocks are definitely lost in loss record 60 of 63
==2807==    at 0x4028876: malloc (vg_replace_malloc.c:236)
==2807==    by 0x808CE4B: SetBufSize(Buf2_tag&, int) (in service-x86-D)
==2807==    by 0x427187D: clone (clone.S:130)

Above log shows that there is a sure memory leak inside function SetBufSize()
The call order of functions is latest/last function call to earliest from top to bottom
i.e. malloc() is called by SetBufSize() which is involked by clone.
A quick code review of that helped catch that major issue of memory leak.

==1983== 864 bytes in 6 blocks are possibly lost in loss record 29 of 64
==1983==    at 0x402732C: calloc (vg_replace_malloc.c:467)
==1983==    by 0x4010C34: allocate_dtv (dl-tls.c:300)
==1983==    by 0x40113D8: _dl_allocate_tls (dl-tls.c:464)
==1983==    by 0x404946C: pthread_create@@GLIBC_2.1 (allocatestack.c:571)
==1983==    by 0x80B6A87: CSxThread::start() (in /home/ad17/p4/TM_ad17/depot/acbu/tropos/personal/dev-1.main.ad17_2/output/x86L/debug/bin/tm_service-X86L-D)

==1983==    by 0x41B3112: (below main) (libc-start.c:226)

This is the next serious level of error. Might be a real issue or might not be.
If the pointer to an dynamically allocated memory is changed/moved, to point to say middle of the allocated memory, it thinks that pointer is lost and flags this error. A code review of the indicated function to understand the pointer operations/arithmetic would throw some light as to whether you should be worried about this error or not.

==2807== 102,392 bytes in 1 blocks are still reachable in loss record 56 of 63
==2807==    at 0x402842F: operator new(unsigned int) (vg_replace_malloc.c:255)
==2807==    by 0x80A532B: MsgQueue::MsgQueue() (in service-x86-D)
==2807==    by 0x41B3112: (below main) (libc-start.c:226)

What above message means is that there is no actual memory leak as such. It means those are the one time memory allocations which are maintained throughout the runtime of the program. This is not any serious memory error.

==1983== Conditional jump or move depends on uninitialised value(s)
==1983==    at 0x80C5D80: MyClass::addRoute(Module_tag, Addr_tag const*, char const*) (in service-x86-D)
==1983==    by 0x80CD876: UdpForward (in service-x86-D)
==1983==    by 0x41B3112: (below main) (libc-start.c:226)

Above could be a issue or a false positive warning. What it is saying is there could be some variable used inside a condition check which it thinks could be uninitialized.

No comments: