当前位置: 首页 > 工具软件 > LeakTracer > 使用案例 >

#内存泄露# #leaktracer# leaktracer设计与实现

艾志尚
2023-12-01

#内存泄露# #leaktracer# leaktracer使用

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

#内存泄露# #leaktracer# leaktracer定制化

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

通过上述文章我们可以学习leaktracer使用leaktracer定制化。在使用的过程 中如果对齐设计原因及实现方式有一定的了解,相信在使用过程中若遇上难道也就能够轻松应对了。

 

leaktracer主要的设计思路为:

  1. 实现一组内存的分配/释放函数,这组函数的函数原型与系统的那一组完全一样,让被trace的library对于内存的分配/释放函数的调用都链接到自己实现的这一组函数中以override掉系统的那组内存/分配释放函数;

  2. 自己实现的这组函数中的内存分配函数记录分配相关的信息,包括分配的内存的大小,callstack等,并调用系统本来的内存分配函数去分配内存;

  3. 自己实现的这组函数中的内存释放函数则销毁内存分配的相关记录,并使用系统的内存释放函数真正的释放内存;

  4. 在trace结束时,遍历所有保存的内存分配记录的信息,并把这些信息保存进文件以供进一步的分析。

override系统内存分配/释放函数

LeakTracer实现的用于override系统内存分配/释放函数的那组函数在AllocationHandlers.cpp中定义:

C++

void* operator new(size_t size)
void* operator new[] (size_t size)
void operator delete (void *p)
void operator delete[]

C

void *malloc(size_t size)
void free(void* ptr)
void* realloc(void *ptr, size_t size)
void* calloc(size_t nmemb, size_t size)

//
// LeakTracer
// Contribution to original project by Erwin S. Andreasen
// site: http://www.andreasen.org/LeakTracer/
//
// Added by Michael Gopshtein, 2006
// mgopshtein@gmail.com
//
// Any comments/suggestions are welcome
//


#include "MemoryTrace.hpp"
#include "LeakTracer_l.hpp"

void* (*lt_malloc)(size_t size);
void  (*lt_free)(void* ptr);
void* (*lt_realloc)(void *ptr, size_t size);
void* (*lt_calloc)(size_t nmemb, size_t size);

void* operator new(size_t size) {
	void *p;
	leaktracer::MemoryTrace::Setup();

	p = LT_MALLOC(size);
	leaktracer::MemoryTrace::GetInstance().registerAllocation(p, size, false);

	return p;
}


void* operator new[] (size_t size) {
	void *p;
	leaktracer::MemoryTrace::Setup();

	p = LT_MALLOC(size);
	leaktracer::MemoryTrace::GetInstance().registerAllocation(p, size, true);

	return p;
}


void operator delete (void *p) {
	leaktracer::MemoryTrace::Setup();

	leaktracer::MemoryTrace::GetInstance().registerRelease(p, false);
	LT_FREE(p);
}


void operator delete[] (void *p) {
	leaktracer::MemoryTrace::Setup();

	leaktracer::MemoryTrace::GetInstance().registerRelease(p, true);
	LT_FREE(p);
}

/** -- libc memory operators -- **/

/* malloc
 * in some malloc implementation, there is a recursive call to malloc
 * (for instance, in uClibc 0.9.29 malloc-standard )
 * we use a InternalMonitoringDisablerThreadUp that use a tls variable to prevent several registration
 * during the same malloc
 */
void *malloc(size_t size)
{
	void *p;
	leaktracer::MemoryTrace::Setup();

	leaktracer::MemoryTrace::GetInstance().InternalMonitoringDisablerThreadUp();
	p = LT_MALLOC(size);
	leaktracer::MemoryTrace::GetInstance().InternalMonitoringDisablerThreadDown();
	leaktracer::MemoryTrace::GetInstance().registerAllocation(p, size, false);

	return p;
}

void free(void* ptr)
{
	leaktracer::MemoryTrace::Setup();

	leaktracer::MemoryTrace::GetInstance().registerRelease(ptr, false);
	LT_FREE(ptr);
}

void* realloc(void *ptr, size_t size)
{
	void *p;
	leaktracer::MemoryTrace::Setup();

	leaktracer::MemoryTrace::GetInstance().InternalMonitoringDisablerThreadUp();

	p = LT_REALLOC(ptr, size);

	leaktracer::MemoryTrace::GetInstance().InternalMonitoringDisablerThreadDown();

	if (p != ptr)
	{
		if (ptr)
			leaktracer::MemoryTrace::GetInstance().registerRelease(ptr, false);
		leaktracer::MemoryTrace::GetInstance().registerAllocation(p, size, false);
	}
	else
	{
		leaktracer::MemoryTrace::GetInstance().registerReallocation(p, size, false);
	}

	return p;
}

void* calloc(size_t nmemb, size_t size)
{
	void *p;
	leaktracer::MemoryTrace::Setup();

	leaktracer::MemoryTrace::GetInstance().InternalMonitoringDisablerThreadUp();
	p = LT_CALLOC(nmemb, size);
	leaktracer::MemoryTrace::GetInstance().InternalMonitoringDisablerThreadDown();
	leaktracer::MemoryTrace::GetInstance().registerAllocation(p, nmemb*size, false);

	return p;
}

 

内存分配函数记录分配相关的信息

inline void registerAllocation(void *p, size_t size, bool is_array);记录每一次内存分配的相关信息

	/** registers new memory allocation, should be called by the
	 *  function intercepting "new" calls */
	inline void registerAllocation(void *p, size_t size, bool is_array);

	/** registers memory reallocation, should be called by the
	 *  function intercepting realloc calls */
	inline void registerReallocation(void *p, size_t size, bool is_array);

inline void registerReallocation(void *p, size_t size, bool is_array);记录每一次内存分配的相关信息,限realloc

	/** registers memory reallocation, should be called by the
	 *  function intercepting realloc calls */
	inline void registerReallocation(void *p, size_t size, bool is_array);

 

// adds all relevant info regarding current allocation to map
inline void MemoryTrace::registerAllocation(void *p, size_t size, bool is_array)
{
	allocation_info_t *info = NULL;
	if (!AllMonitoringIsDisabled() && (__monitoringAllThreads || getThreadOptions().monitoringAllocations) && p != NULL) {
		MutexLock lock(__allocations_mutex);
		info = __allocations.insert(p);
		if (info != NULL) {
			info->size = size;
			info->isArray = is_array;
			storeTimestamp(info->timestamp);
		}
	}
 	// we store the stack without locking __allocations_mutex
	// it should be safe enough
	// prevent a deadlock between backtrave function who are now using advanced dl_iterate_phdr function
 	// and dl_* function which uses malloc functions
	if (info != NULL) {
		storeAllocationStack(info->allocStack);
	}

	if (p == NULL) {
		InternalMonitoringDisablerThreadUp();
		// WARNING
		InternalMonitoringDisablerThreadDown();
	}
}


// adds all relevant info regarding current allocation to map
inline void MemoryTrace::registerReallocation(void *p, size_t size, bool is_array)
{
	if (!AllMonitoringIsDisabled() && (__monitoringAllThreads || getThreadOptions().monitoringAllocations) && p != NULL) {
		MutexLock lock(__allocations_mutex);
		allocation_info_t *info = __allocations.find(p);
		if (info != NULL) {
			info->size = size;
			info->isArray = is_array;
			storeAllocationStack(info->allocStack);
			storeTimestamp(info->timestamp);
		}
	}

	if (p == NULL) {
		InternalMonitoringDisablerThreadUp();
		// WARNING
		InternalMonitoringDisablerThreadDown();
	}
}

内存释放函数则销毁内存分配的相关记录

 inline void registerReallocation(void *p, size_t size, bool is_array);记录每一次内存释放的相关信息

	/** registers new memory allocation, should be called by the
	 *  function intercepting "new" calls */
	inline void registerAllocation(void *p, size_t size, bool is_array);

 


// removes allocation's info from the map
inline void MemoryTrace::registerRelease(void *p, bool is_array)
{
	if (!AllMonitoringIsDisabled() && __monitoringReleases && p != NULL) {
		MutexLock lock(__allocations_mutex);
		allocation_info_t *info = __allocations.find(p);
		if (info != NULL) {
			if (info->isArray != is_array) {
				InternalMonitoringDisablerThreadUp();
				// WARNING
				InternalMonitoringDisablerThreadDown();
			}
			__allocations.release(p);
		}
	}
}

遍历所有保存的内存分配记录的信息,并把这些信息保存

void writeLeaksToFile(const char* reportFileName);保存内存分配记录信息到文件。 

	/** writes report with all memory leaks */
	void writeLeaksToFile(const char* reportFileName);
// writes all memory leaks to given stream
void MemoryTrace::writeLeaksToFile(const char* reportFilename)
{
	MutexLock lock(__allocations_mutex);
	InternalMonitoringDisablerThreadUp();

	std::ofstream oleaks;
	if (!isFolderExist(reportFilename)) {
		createDirectory(reportFilename);
	}
	if (__allocations.empty()) {
		return; //no memory leak, not need to create leak file
	}

	oleaks.open(reportFilename, std::ios_base::out);
	if (oleaks.is_open())
	{
		writeLeaksPrivate(oleaks);
		oleaks.close();
	}
	else
	{
		std::cerr << "Failed to write to \"" << reportFilename << "\"\n";
	}
	InternalMonitoringDisablerThreadDown();
}

遍历自定义MapMemoryInfo 中所有元素。

// writes all memory leaks to given stream
void MemoryTrace::writeLeaksPrivate(std::ostream &out)
{
	struct timespec mono, utc, diff;
	allocation_info_t *info;
	void *p;
	double d;
	const int precision = 6;
	int maxsecwidth;

	clock_gettime(CLOCK_REALTIME, &utc);
	clock_gettime(CLOCK_MONOTONIC, &mono);

	if (utc.tv_nsec > mono.tv_nsec) {
		diff.tv_nsec = utc.tv_nsec - mono.tv_nsec;
		diff.tv_sec = utc.tv_sec - mono.tv_sec;
	} else {
		diff.tv_nsec = 1000000000 - (mono.tv_nsec - utc.tv_nsec);
		diff.tv_sec = utc.tv_sec - mono.tv_sec -1;
	}

	maxsecwidth = 0;
	while(mono.tv_sec > 0) {
		mono.tv_sec = mono.tv_sec/10;
		maxsecwidth++;
	}
	if (maxsecwidth == 0) maxsecwidth=1;

	out << "# LeakTracer report";
	d = diff.tv_sec + (((double)diff.tv_nsec)/1000000000);
	out << " diff_utc_mono=" << std::fixed << std::left << std::setprecision(precision) << d ;
	out << "\n";

	__allocations.beginIteration();
	while (__allocations.getNextPair(&info, &p)) {
		d = info->timestamp.tv_sec + (((double)info->timestamp.tv_nsec)/1000000000);
		out << "leak, ";
		out << "time="  << std::fixed << std::right << std::setprecision(precision) << std::setfill('0') << std::setw(maxsecwidth+1+precision) << d << ", "; // setw(16) ?
		out << "stack=";
		#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
		out << "size=" << info->size << ", ";

		out << "data=";
		const char *data = reinterpret_cast<const char *>(p);
		for (unsigned int i = 0; i < PRINTED_DATA_BUFFER_SIZE && i < info->size; i++)
			out << (isprint(data[i]) ? data[i] : '.');
		out << '\n';
	}
}

整体来看LeakTracer的设计与实现都并不复杂,因而能够trace的memory issue也就有限。比如,LeakTracer就无法trace多次释放等问题。但我们可以通过源码编写更强大的内存相关的trace工具。

 类似资料: