Linux C/C++ memory leak detection

principle

Memory leak concept

Memory leak : In computer science, the program's failure to release memory that is no longer used due to negligence or error is called a memory leak. Memory leak does not mean the physical disappearance of the memory, but after the application allocates a certain segment of memory, due to a design error, the program is not released, which causes a waste of memory.

Insert picture description here

Detection principle

To achieve memory leak detection, most likely to think that the program memory to operate (EG: 申请内存, 释放内存etc.) recorded during the control it when the program exits the application and release correspond get on, you can achieve memory leak detection. It is not difficult to say, but how do we monitor the operation of the program on the memory? This will be discussed in the specific way below.

So to extend it, how to detect it 野指针?

Insert picture description here
According to my shallow knowledge, currently available only know 内存涂鸦the way that each application memory, to be uninitialized graffiti on its content. If the pointer is found to be an uninitialized graffiti value when using the pointer, it is proved that the pointer is 野指针.

In the same way, when we release the memory, we need to do memory graffiti on it, so that when we call the pointer again, we find that the memory it points to is graffitied 未初始化, and we know that it is using a wild pointer.

As far as I know, some compilers do this, such as VC++ .

Method to realize

The above principle sounds easy, but how do you specifically record the memory operations of a program?

The simpler ones are the following three methods:

Overload

As we wrote in the program newand deletewhile we actually called is C++built into the language new operatorand delete operator. So that we can override new operatorand delete operatorto implement memory allocation and release the case of recording and analysis.

This method was mentioned in the previous blog post: C++-Overloaded Operators for Array Out-of-Bound Detection & Dividend Detection . Here I only inform you that you need to rewrite the following functions:

void* operator new( size_t nSize, char* pszFileName, int nLineNum )
void* operator new[]( size_t nSize, char* pszFileName, int nLineNum )
void operator delete( void *ptr )
void operator delete[]( void *ptr )

The shortcomings of this method are also obvious, it is just equivalent to encapsulating a layer of library functions. In this layer, the record analysis of the memory is realized.

For the original project (direct new operator& delete operatoretc.) need to be new( ), delete( )such as a. A modification, only to realize memory test.
Insert picture description here

gcc_wrap

There is no way out of mountains and rivers

GCC reserved a back door for us, leaving us with an interesting option–wrap=symbol

We may call it 包装函数; it can make the link in the link symbol符号would search for when __wrap_symbol, when will not find the original link symbol符号.

Here is one demo:

/* test.cpp */
#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>

extern "C"
{
    
    
	int malloc_count = 0;
	int free_count = 0;

	void *__real_malloc(size_t size);
	void __real_free(void *ptr);
	void print_trace ();

	void *__wrap_malloc(size_t size){
    
    
		malloc_count++;
		print_trace();
		return __real_malloc(size);
	}
	void __wrap_free(void *ptr){
    
    
		free_count++;
		print_trace();
		__real_free(ptr);
	}

	void *operator new(size_t size){
    
    
		return __wrap_malloc(size);
	}

	void operator delete(void *ptr){
    
    
		__wrap_free(ptr);
	}

	void print_trace () 
	{
    
    
		printf ("========== call stack =========\n");
		void *array[10];
		int size; 
		char **strings; 
		int i;
		size = backtrace (array, 10);
		strings = backtrace_symbols (array, size);
		if (NULL != strings)
		{
    
    
			printf ("Obtained %zd stack frames.\n", size);
			for (i = 0; i < size; i++)
			{
    
    
				printf ("%s\n", strings[i]);
			}
			__real_free(strings); 
			strings = NULL; 
		}
	}
}

int main(){
    
    

	int *p1 = (int*)malloc((sizeof(int)));
	int *ptr1 = new int[5];
	int *ptr2 = new int[5];
	delete ptr1;

	printf ("========== memory leak detection =========\n");

	printf("malloc_count = %d\n",malloc_count);
	printf("free_count = %d\n",free_count);

	if(malloc_count != free_count){
    
    
		printf("memony leak!\n");
	}

	return 0;
}

print_trace( ) Print stack, no need

Remember to bring that interesting option when compiling

gcc test.cpp -Wl,--wrap=malloc --wrap=free

LD_PRELOAD Steal the beam and change the column

Always felt that the use of gcc-wrapthe method also is not convenient enough, can not be packaged as so库of any desired memory function detects the link compile it?

Insert picture description here
Reachable duck means: Yes

Prior to this, we first look at the Linux系统loading so库process:

Following switched Zheng Han Andrew_Hann - Linux System Calls Hooking Method Summary

Including Linuxsystems, including many open-source systems are based on Glibcdynamic links ELF可执行文件will also launch at startup 动态链接器(/lib/ld-linux.so.X), the program relies on shared objects all by the dynamic linker is responsible for loading and initializing , so the search process here called shared libraries, nature The above is the search process of the shared library path by the dynamic linker (/lib/ld-linux.so.X) . The search process is as follows:

  • /etc/ld.so.cache : LinuxTo speed up LD_PRELOADthe search process, the establishment of a system ldconfig程序, this program is responsible for:
    • Maintain each shared library under the shared library SO-NAME(one-to-one corresponding symbolic link), so that the SO-NAME of each shared library can point to the correct shared library file
    • All SO-NAME collected, concentrated into /etc/ld.so.cachea file inside, and create a buffer SO-NAME
    • When the dynamic linker wants to find a shared library, it can find it directly from /etc/ld.so.cache. So, if we add in the system specified shared library directory, delete or update any shared library, or we change /etc/ld.so.conf, /etc/ld.preloadconfiguration, should be run once ldconfigthis program to update the SO-NAME and /etc/ld.so. cache
  • Search (LD_PRELOAD) according to /etc/ld.so.preload configuration : the configuration file is saved in the shared library path to be searched, Linux dynamic loader shared libraries according to the order of progressive breadth search
  • According to the dynamic library search path specified by the environment variable LD_LIBRARY_PATH
  • According to the configuration information ELF file : Any dynamically linked modules depend module path stored in ".dynamic"段the by DT_NEEDindicating the type of item, the dynamic linker will follow this path to find DT_RPATHthe time specified path, compiled object code, can gccAdd link parameters to -Wl,-rpathspecify the dynamic library search path.
    • The absolute path is saved in the DT_NEED section, and the dynamic linker directly loads according to this path
    • The relative path is saved in the DT_NEED section, and the dynamic linker will search the following paths for the library files in an agreed order
      • /lib
      • /usr/lib
      • Configure the specified search path in /etc/ld.so.conf

Summary : you can see, LD_PRELOADit is Linux系统the first to launch a new process 加载soof the search path, so it can affect the operation of the program when the link ( Runtime linker), which allows you to define before running "priority loading" dynamic link library.

We just by LD_PRELOADloading .sothe preparation we need hooknamesake functions, introduced into the process according to the global symbol table symbol Linux external dynamic shared library, after the introduction of the symbols will be omitted, i.e., the original system, .so(/lib64/libc.so.6)the symbols will be omitted.

By strace programcan see, Linuxis a priority load LD_PRELOADspecified .so, then load the system default .sois.

/* testmallic.cpp*/
#include <stdio.h>
#include <stdlib.h>

int malloc_count = 0;
int free_count = 0;

void printf_end();

void printf_end(){
    
    
	fprintf(stderr,"=========Result=========\n");
	fprintf(stderr,"malloc_count = %d\n",malloc_count);
	fprintf(stderr,"free_count = %d\n",free_count);
	if(malloc_count != free_count){
    
    
		fprintf(stderr,"Memlook\n");
	}
}

void* malloc(size_t size){
    
    
	if(0 == malloc_count){
    
    
		atexit(printf_end);
	}
	malloc_count ++;
	fprintf(stderr,"malloc_count = %d\n",malloc_count);
	return realloc(NULL,size);
}

void free(void *p){
    
    
	free_count ++;
	fprintf(stderr,"free_count = %d\n",free_count);
	realloc(p,0);
}

Compiled to so库

g++ -shared -fpic -o testmallox.so testmallic.cpp

Write another test case

/*main.cpp*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){
    
    
    char *c1 = (char*)malloc(sizeof(char));
    int *c2 = (int*)malloc(sizeof(int));
	free(c1);
	return 0;

}

Compile it
g++ -o main main.cpp

run
LD_PRELOAD=./testmalloc.so ./main

Guess you like

Origin blog.csdn.net/weixin_40774605/article/details/105957547