#內存泄露# #leaktracer# leaktracer定製化

#內存泄露# #leaktracer# leaktracer使用

https://blog.csdn.net/xiaoting451292510/article/details/105847121

#內存泄露# #leaktracer# leaktracer設計與實現

https://blog.csdn.net/xiaoting451292510/article/details/105850409

通過上述文章我們可以輕鬆理解leaktracer使用leaktracer設計與實現。在實際使用過程中,基本上都會碰到一些小問題。需要對leaktracer進行定製化修改。以下的定製化請根據各自項目實際需求酌情進行修改。

  • callstack深度問題

callstack深度默認只有5個。

#ifndef ALLOCATION_STACK_DEPTH
#	define ALLOCATION_STACK_DEPTH 5
#endif

因此當callstack深度較大時無法顯示詳細的調用關係。如下:

stack=0x7f05d63801d5 0x7f05d638e594 0x7f05d638d8f8 0x7f05d638c28d 0x7f05d6382738

  • callstatck地址問題

無論是leak-analyze-addr2line,leak-analyze-gdb二個工具進行解析。或者藉助gdb、objdump或map文件等手段得到該泄露源的真正文件/行號或函數範圍。因多個動態庫及其他原因,或多或少都會碰上各種庫引用等問題導致???。會很麻煩


$ ./helpers/leak-analyze-addr2line memleak_test_so leaks.out 
Processing "leaks.out" log for "memleak_test_so"
Matching addresses to "memleak_test_so"
found 49 leak(s)
100 bytes lost in 1 blocks (one of them allocated at 1444.194222), from following call stack:
	??:0
	/home/cll/99_temp/memory_leak/leaktracer/memleak_way1/memleak_test.cpp:110
	??:0
	??:?
400 bytes lost in 1 blocks (one of them allocated at 1444.194550), from following call stack:
	??:0
	/home/cll/99_temp/memory_leak/leaktracer/memleak_way1/memleak_test.cpp:110
	??:0
	??:?
328 bytes lost in 1 blocks (one of them allocated at 1434.190960), from following call stack:
	/home/cll/99_temp/memory_leak/leaktracer/memleak_way1/memleak_test.cpp:83
	??:0
	??:?
  • 文件路徑不存在無法生成文件問題

使用void writeLeaksToFile(const char* reportFileName);接口生成文件時,若文件夾路徑不存在,則會生成文件失敗。然而實際在較大工程中使用時,均會將內存泄露文件統一到某個指定路徑。可能存在文件夾路徑不存在的情況。

  • 未內存泄露確生成文件問題

在較大工程中使用時,進程數較多。若未內存泄露確產生文件,對UI交互及直觀上總有些不協調。

 

callstack深度問題

將默認callstack深度5修改爲較大值,本文是50。請根據實際情況調整,一般情況下對整個情況影響不大。

#ifndef ALLOCATION_STACK_DEPTH
#	define ALLOCATION_STACK_DEPTH 50
#endif

callstatck地址問題

通過源碼,我們很容易可以找到callstatck地址實際是通過int backtrace(void **buffer, int size);接口獲取,既然有了地址,我們一樣可以通過 char **backtrace_symbols(void *const *buffer, int size);接口將backtrace函數獲取的信息轉化爲一個字符串數組. 參數buffer應該是從backtrace函數獲取的數組指針,size是該數組中的元素個數(backtrace的返回值),函數返回值是一個指向字符串數組的指針,它的大小同buffer相同.每個字符串包含了一個相對於buffer中對應元素的可打印信息.它包括函數名,函數的偏移地址,和實際的返回地址。這裏我使用了編譯宏BACKTRACE_SYMBOLS_USED將原生隔離開了。

		#ifdef BACKTRACE_SYMBOLS_USED
		unsigned int i_depth = 0;
		for (i_depth = 0; i_depth < ALLOCATION_STACK_DEPTH; i_depth++) {
			if (info->allocStack[i_depth] == NULL) break;

			if (i_depth > 0) out << ' ';
			out << info->allocStack[i_depth];
		}
		out << '\n';
		char **trace_symbols = (char **)backtrace_symbols (info->allocStack, i_depth);
		if (NULL != trace_symbols) {
			size_t name_size = 64;
			char *name = (char*)malloc(name_size);
			for (unsigned int j = 0; j < i_depth; j++) {
				char *begin_name = 0;
				char *begin_offset = 0;
				char *end_offset = 0;
				for (char *p = trace_symbols[j]; *p; ++p) {
					if (*p == '(') {
						begin_name = p;
					} else if (*p == '+' && begin_name) {
						begin_offset = p;
					} else if (*p == ')' && begin_offset) {
						end_offset = p;
						break;
					}
				}
				if (begin_name && begin_offset && end_offset ) {
					*begin_name++ = '\0';
					*begin_offset++ = '\0';
					*end_offset = '\0';
					int status = -4;
					char *ret = abi::__cxa_demangle(begin_name, name, &name_size, &status);
					if (0 == status) {
						name = ret;
						out << trace_symbols[j] << ":" << name << "+" << begin_offset;
					} else {
						out << trace_symbols[j] << ":" << begin_name << "()+" << begin_offset;
					}
				} else {
					out << trace_symbols[j];
				}
				out << '\n';
			}
			free(trace_symbols);
		}
		#else
		for (unsigned int i = 0; i < ALLOCATION_STACK_DEPTH; i++) {
			if (info->allocStack[i] == NULL) break;

			if (i > 0) out << ' ';
			out << info->allocStack[i];
		}
		out << ", ";
		#endif

文件路徑不存在無法生成文件問題

增加bool isFolderExist(const char * folder);    int32_t createDirectory(const char* directoryPath);接口,並在memory leak report時即void MemoryTrace::writeLeaksToFile(const char* reportFilename)實現中加入。

	//////////////////////////////////////////////////////////////////////////////////////////////////
	bool isFolderExist(const char * folder);
	int32_t createDirectory(const char* directoryPath);
	//////////////////////////////////////////////////////////////////////////////////////////////////
	if (!isFolderExist(reportFilename)) {
		createDirectory(reportFilename);
	}
//////////////////////////////////////////////////////////////////////////////////////////////////
//#include  <io.h> //_access fun
//#include <direct.h> //_mkdir fun
#include <unistd.h> //access fun
#include <sys/stat.h>
bool MemoryTrace::isFolderExist(const char * folder)
{
	int ret = 0;
 
//	ret = _access(folder, 0);
	ret = access(folder, 0);
	if (ret == 0)
		ret = true;
	else
		ret = false;
 
	return ret;
}
 
int32_t MemoryTrace::createDirectory(const char* directoryPath)
{
	uint32_t dirPathLen = 0;
	if (directoryPath != NULL) {
		dirPathLen = strlen(directoryPath);
	}
	if (dirPathLen > FILENAME_MAX)
	{
		return -1;
	}
	char tmpDirPath[FILENAME_MAX] = { 0 };
	for (uint32_t i = 0; i < dirPathLen; ++i)
	{
		tmpDirPath[i] = directoryPath[i];
		if (tmpDirPath[i] == '\\' || tmpDirPath[i] == '/')
		{
			if (!isFolderExist(tmpDirPath))
			{
//				int ret = _mkdir(tmpDirPath);
				int ret = mkdir(tmpDirPath, 0755);
				//BOOL ret = CreateDirectory(tmpDirPath, NULL);
				if (ret != 0)
					return -1;
			}
		}
	}
	return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////

 

未內存泄露確生成文件問題

在memory leak report時即void MemoryTrace::writeLeaksToFile(const char* reportFilename)實現中加入判斷

	if (__allocations.empty()) {
		return; //no memory leak, not need to create leak file
	}

Future

同樣,還可以使用addr2line解析到具體哪行代碼,由於addr2line命令並非在各平臺都包含此命令,因此並不具有通用性。請自行根據實際情況進行添加。

addr2line找到對應的代碼行,有以下前提條件是:

  • 該可執行程序用-g編譯,帶調試信息(所謂調試信息就是代碼和地址的一個對應關係的信息)。
  • 如果crash在一個so裏面,那addr2line不能直接給出代碼行。因爲我們都知道,so裏面的地址在可執行文件裝載的時候,是可以被 reallocate的。在windows核心編程中說dll的加載邏輯的時候,也提到過。所以,如果只有一個so的地址,要找出對應代碼行的話,就要給 addr2line一個基地址和偏移量,或者根據可執行程序的smap信息,自己將這個地址轉化成相對於so基地址的一個偏移地址才行。

 

生成memory leak文件內容如下:

# LeakTracer report diff_utc_mono=1588164717.365891
leak, time=47808.237034, stack=0x7f80b05bd0d4 0x7f80af8411d5 0x7f80af84f594 0x7f80af84e8f8 0x7f80af84d28d 0x7f80af843738 0x40129c 0x7f80af7f4830 0x401149
./libleaktracer.so:malloc()+0x134
/lib/x86_64-linux-gnu/libc.so.6:_IO_file_doallocate()+0x55
/lib/x86_64-linux-gnu/libc.so.6:_IO_doallocbuf()+0x34
/lib/x86_64-linux-gnu/libc.so.6:_IO_file_overflow()+0x1c8
/lib/x86_64-linux-gnu/libc.so.6:_IO_file_xsputn()+0xad
/lib/x86_64-linux-gnu/libc.so.6:_IO_puts()+0xa8
./memleak_test_so() [0x40129c]
/lib/x86_64-linux-gnu/libc.so.6:__libc_start_main()+0xf0
./memleak_test_so() [0x401149]
size=1024, data=***********************************************...
leak, time=47818.239252, stack=0x7f80b05bd0d4 0x7f80b03b6c58 0x4015d5 0x7f80af7f4830 0x401149
./libleaktracer.so:malloc()+0x134
./libmemleak_test.so:memory_leak_test2()+0x2c
./memleak_test_so() [0x4015d5]
/lib/x86_64-linux-gnu/libc.so.6:__libc_start_main()+0xf0
./memleak_test_so() [0x401149]
size=100, data=..................................................
leak, time=47808.237063, stack=0x7f80b05bd0d4 0x4012b7 0x7f80af7f4830 0x401149
./libleaktracer.so:malloc()+0x134
./memleak_test_so() [0x4012b7]
/lib/x86_64-linux-gnu/libc.so.6:__libc_start_main()+0xf0
./memleak_test_so() [0x401149]
size=100, data=..................................................
leak, time=47818.239039, stack=0x7f80b05bd0d4 0x7f80b03b6a5c 0x4015d0 0x7f80af7f4830 0x401149
./libleaktracer.so:malloc()+0x134
./libmemleak_test.so:memory_leak_test1()+0x2c
./memleak_test_so() [0x4015d0]
/lib/x86_64-linux-gnu/libc.so.6:__libc_start_main()+0xf0
./memleak_test_so() [0x401149]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章