So far, the need for memory leak detection has been largely met by the method of wrap malloc, new function overloading, and calculation of pointer memory size.
If a memory leak is found, then find out where the memory leaks are and fix it.
Boundless code, how to find? It would be nice if we could find the place to apply it based on the memory that was not released.
We are going to do it today.
To identify the applicant's information based on the memory address, the mapping between the address and the applicant should be established at the beginning of the application.
1. Memory address
Memory address, is a unsigned long value, used void *
to store also can. In order to avoid type conversions, I used the void *
.
2. Applicants ' information
The applicant's information is more complex, not a type can be done. What does it include?
In the case of C, it is important to know who called it __wrap_malloc
. But in the case of C + +, the call __wrap_malloc
must be new, which doesn't make sense, and you need to know who called New. Further, new is probably called in the constructor, so it is very likely that we really need to know who called the constructor.
This shows that just knowing who is calling is __wrap_malloc
not enough, what we need is the entire stack of information.
The entire stack contains a lot of content, where we only record the depth (int) of the stack and the symbol name of each layer (char * *). The symbol name is unique throughout the program (whether C or C + +) and the relative position is deterministic (except for the dynamic library), and when the program ends, the caller's file name and line number are re-introduced according to the symbol name.
Why not get the file name and line number directly?
Because the implementation of the symbol name is relatively simple.
3. Mapping method
When it comes to mapping, the first thing to think about is map, hash, and so on.
However, it is necessary to note that this is a __wrap_malloc
function that is bound to go where each program dynamically allocates space.
What does it matter? Imagine what it would be like to have a dynamic request for memory to come into this function and accidentally apply a memory in this function. In -Wl,--wrap,malloc
the role of coming here again, so opened the "chicken eggs, eggs and chickens" in the death cycle, until--stack overflow.
Therefore, in this function can use only the stack space or global space, if you must use heap space, also must be shown to use instead of __real_malloc
new or malloc. Because of the inevitable use of dynamic memory space in map, hash, or discard it.
What do we do? To avoid complications, I used the simplest but somewhat stupid method-arrays.
struct memory_record{ void * addr; size_t count; int depth; char **symbols;}mc[1000];
4. How do I get the symbols in the stack?
GCC gives us the appropriate function to call on the line as required.
char * stack [20 ] = {0 };mc[i]. depth = BackTrace (reinterpret_cast <void * * > (stack ), sizeof ( stack ) /sizeof (stack [0 ])); if (mc[i].depth) {mc[i].symbols = Backtrace_symbols (reinterpret_cast < void **> (stack ), mc[i].depth); }
The BackTrace function is used to get the depth () of the stack depth
, as well as the stack address () for each layer stack
.
backtrace_symbols
The function returns the symbol name () based on the stack address symbols
.
It is important to note that Backtrace_symbols returns an array of symbols, the space of this array is backtrace_symbols
allocated by the caller but needs to be freed.
Why backtrace_symbols
is the memory allocated here without causing stack overflow? Here's my guess:
backtrace_symbols
The functions and wrap mechanisms are all provided by the GNU, attribute relatives. Since it is a relative, it is possible to make backtrace_symbols
use of the memory directly by bypassing the wrap mechanism.
Source:
#include <iostream>using namespace STD;#include "string.h"#include <stdio.h>#include <malloc.h>#include <execinfo.h>#if (defined (_x86_) &&!defined (__x86_64))#define _ALLOCA_S_MARKER_SIZE 4#elif defined (__ia64__) | | defined (__X86_64)#define _ALLOCA_S_MARKER_SIZE 8#endifsize_t count =0;intBackTrace (void**buffer,intsize);structmemory_record{void* ADDR; size_t count;intDepthChar**symbols;} mc[ +];extern "C"{void* __REAL_MALLOC (intc);void* __WRAP_MALLOC (size_t size) {void*p = __real_malloc (size); size_t w = * ((size_t*) ((Char*) (P-_alloca_s_marker_size));cout<<"malloc"<<p<<endl; for(inti =0; I < +; i++) {if(Mc[i].count = =0) {count + = W; MC[I].ADDR = p; Mc[i].count = W;Char*Stack[ -] = {0}; Mc[i].depth = BackTrace (reinterpret_cast<void**> (Stack),sizeof(Stack)/sizeof(Stack[0]));if(mc[i].depth) {Mc[i].symbols = Backtrace_symbols (reinterpret_cast<void**> (Stack), mc[i].depth); } Break; } }returnP;}void__real_free (void*PTR);void__wrap_free (void*PTR) {cout<<"Free"<<ptr<<endl; size_t w = * ((size_t*) ((Char*) (PTR-_alloca_s_marker_size)); for(inti =0; I < +; i++) {if(mc[i].addr = = ptr) {Mc[i].count-= W; Count-= W;if(mc[i].symbols) __real_free (mc[i].symbols); Break; }} __real_free (PTR);}}void*operator New(size_t size) {return malloc(size);}void operator Delete(void*PTR) { Free(PTR);}voidPrint_leaked_memory () {if(Count! =0)cout<<"Memory leak!"<<endl; for(inti =0; I < +; i++) {if(Mc[i].count! =0) {cout<<mc[i].addr<<"'<<mc[i].count<<endl;if(Mc[i].symbols) { for(size_t j =0; J < Mc[i].depth; J + +) {printf("===[%d]:%s\n", (j+1), Mc[i].symbols[j]); }} __real_free (Mc[i].symbols); } }}classa{int*P1; Public: A () {p1 =New int;} ~a () {Deletep1;}};intMainvoid){memset(MC,0,sizeof(MC)); Count =0;int*P1 =New int(4);int*P2 =New int(5);DeleteP1; Print_leaked_memory ();return 0;}
Compile command:
g++-o test test.-g-Wl,---Wl,--wrap,free
Run:
"==="-d"["-f-d"]"-e test
Method Analysis:
Advantages:
(1) At the end of the program run, the print program memory leaks and the code that causes the leak occurs the file and line number
(2) C + + is applicable
(3) Need to modify the product source code to achieve the function
(4) all. O and static libraries are valid for all links.
Disadvantages:
(1) Not applicable to dynamic library
(2) The stack information and the file name line number is two operations, can not solve the problem at once
Detection of memory leaks in Linux (v) code to record memory leaks