#內存泄露# #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]