LINUX GDB: IDENTIFY MEMORY LEAKS
dalla 270000SVT2 Visits (17992)
This small article describe how to track memory leaks using 'gdb' on Linux. If you are using products like 'db2' or any other product that has it's own memory management routines then you should first track possible leaks at the product level using the tools it provides. For 'db2' that would be 'db2pd' for example. This article therefore applies only to memory leaks at the 'malloc()' level, that is blocks of memory allocated using 'malloc()' but never freed.
Remember that a debugger is very slow and might cause performance issues. If you can 'overload' the default 'malloc()/free()' routines via LD_PRELOAD or another way it would probably be faster and easier than using the debugger.
NOTE: The 'gdb' script provided below can be modified as fits. Remember that
If you are still reading this means you have no other choice but using the debugger. So let's first describe what we need to do.
- Everytime we enter 'malloc()' we should 'save' the memory allocation
Note that while the 'size' is not needed it is good to have it because it might allow you to find some 'pattern' in the list of allocated blocks. Since we don't want to have to manually interact with the debugger each time a memory allocation is made or freed we want to put command in s script that gdb will take as an argument and execute without any manual intervention
== gdbcmd1 ==
set pagination off
Then we attach a running process with the debugger using the script:
# gdb --command=gdbcmd1 server2 11543
The program will run and send the output to a file named 'gdbcmd1.out'. Because the file contains as well 'gdb' messages, besides the 'printf' we added to the script we can get a clearer output by doing this:
# grep -e "^malloc" -e "^free" gdbcmd1.out
We have something like this:
malloc(57) = 0x0000000012e1b260
Once you have that you can parse the output to find out those blocks that are never freed. Later on you can add a 'where' command in the commands for the entry in 'malloc()' breakpoint to see where the allocation comes from. So you would for example replace this:
Now some words about the 'gdb' script 'gdmcmd1' itself:
set pagination off
- This prevents to have to press 'return' each time the screen has been
set breakpoint pending on
- In case the library containing the function would not yet be loaded
set logging file gdbcmd1.out
- Instruct 'gdb' to save the output to a file named 'gdbcmd1'.
set logging on
- Instruct 'gdb' to start sending output to 'gdbcmd1'.
- Place a 'hardware assisted' breakpoint on the entry of malloc. Save the
- Place a 'hardware assisted' breakpoint on the return from malloc.
- Finding the 'return' instruction offset from malloc -
We could use the 'finish' instruction of 'gdb' but in many cases it simply did not work as expected for me. So this way of doing it is an alternative. To find the return instruction offset in malloc you need to check the assembly for malloc (disas malloc in gdb) and locate the 'retq' (64 bits) instruction. Then you take the address of that instruction and compute the offset starting from the first instruction in malloc. For example:
(gdb) disas malloc
0x0000003f8a674063 - 0x0000003f8a673fb0 = 179
Once again you might have to play a bit with the script to obtain the results