⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 memsnapshot.cpp

📁 一个内存调试工具,给需要的coder 喜欢就下
💻 CPP
字号:
/*********************************************************************************** * filename	: MemSnapShot.cpp * author	: mjguan * date		: 2003/04/22 * description	: receive message( data ) from the message queue, and analysis it. *  * 1. 使用curses库写屏幕后,应该调用refresh()来刷新。 ***********************************************************************************/#include <curses.h>#include <unistd.h>#include <errno.h>#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <sys/types.h>#include <sys/msg.h>#include <pwd.h>#include <assert.h>#include <map>#include <list>using namespace std;#define SINGLE_NEW		0x00		// indicate alloc memory with new type#define ARRAY_NEW		0x01		// indicate alloc memory with new[] type#define SINGLE_DELETE		0x02		// indicate free memory with delete type#define ARRAY_DELETE		0x03		// indicate free memory with delete[] type#define FILENAME_LENGTH		32		// the filename length#define MEMORY_INFO		0X12345678	// indicate the message type on the message queuetypedef struct{	char		Filename[ FILENAME_LENGTH ];	// new所在的源程序文件名	unsigned long	LineNum;	// new在源文件中的行号	size_t 		AllocSize;	// 分配的内存大小	int		OperationType;	void *		pBuffer;	// 分配后得到的内存指针	short		errCode;	// 0 - 没有释放, 1 - delete了new[]分配的内存}MemOperation;typedef struct {	int	Type;			// message type, in this module must be MEMORY_INFO	MemOperation Data;		// content of memory operation} MsgBuffer;typedef struct{	char Filename[ FILENAME_LENGTH ];	unsigned long LineNum;	void *MemoryPtr;	int OperationType;	unsigned long TotalSize;} StatResult;int nMsgQueue;	// message queue idint interval = 1;pid_t pid = 0;map<void*, StatResult> mapMemory;list<StatResult> listMemory;pthread_mutex_t mutexMap;extern int errno;pthread_t tid1, tid2, tid3;unsigned long int totalLeak = 0;WINDOW *win;	void *AnalyseMessage( void * );void *DisplayStatResult( void * );void *MonitorKeyboard( void * );void initial();void popWin( char *str );void DecodeErr( int ErrNum );intmain( int argc, char *argv[] ){	key_t key;	int flags, counter = 0, ch;	char msgpath[ 64 ], str[ 16 ], *p;	struct passwd *pwd;	bool bCorrect = false;	struct msqid_ds	msgInfo;	MsgBuffer ReceiveMsg;		while( 1 )	{			if ( -1 == (ch = getopt( argc, argv, "pt" ) ) )			break;			switch( ch )		{		case 'p':			p = argv[ optind ];			// 判断输入的PID的长度的合法性			if ( strlen( p  ) > 11 )			{				printf( "你输入的PID数值太大了, 请检查后再试\n" );				exit( 1 );			} 			while ( *p )			{				if ( *p < '0' || *p > '9' )				{					printf( "你输入的PID含有非法字符, 请检查后再试\n" );					exit( 1 );				}				p++;			}			pid = atol( argv[ optind ] );			bCorrect = true;			break;		case 't':			p = argv[ optind ];			// 判断输入的PID的长度的合法性			if ( strlen( p  ) > 3 )			{				printf( "你输入的时间间隔数值太大了, 请检查后再试\n" );				exit( 1 );			} 			while ( *p )			{				if ( *p < '0' || *p > '9' )				{					printf( "你输入的时间间隔含有非法字符, 请检查后再试\n" );					exit( 1 );				}				p++;			}			interval = atol( argv[ optind ] );			if ( interval < 1 )				interval = 1;			if ( interval > 30 )				interval = 30;			break;		default:			break;		}		}	if ( false == bCorrect )		printf( "usage: MemSnapShot -p pid [ -t interval ]\n" ), exit( 1 );			pwd = getpwuid( getuid() );	sprintf( msgpath, "%s/.MemMsgQueue%d", pwd->pw_dir, pid );	// 确保m_szMsgPath表示的文件存在, 否则计算创建消息队列成功	// 其key值也将是0XFFFFFFFF(-1)。	// 如果文件不存在则创建, 创建不成功则尝试10次	do	{		// Open for reading and appending (writing at end  of  file). The		// file  is created if it does not exist		FILE *fp = fopen( msgpath, "r" );		if ( fp )			break;	} while( counter++ < 10 );	if ( counter == 10 )		exit( 0 );	key  =  ftok( msgpath, 'a' );			nMsgQueue  =  msgget( key, 0 );	if( nMsgQueue == -1 ) // the message queue exist then delete it	{		switch( errno )		{		case EIDRM:			printf( "消息队列已经被删除\n" );			break;		case EACCES:			printf( "消息队列存在, 但是你没有访问权限\n" );			break;		default:			printf( "错误, 可能的原因:\n"				"1. 输入的进程(ID:%d)不存在\n"				"2. 输入的进程(ID:%d)中没有含有消息队列\n"				"3. 你与运行进程(ID:%d)的用户不是同一个人\n",				pid, pid, pid );			break;		}		exit( 1 );	}		initial();		// 清空当前消息队列中	if ( 0 != msgctl( nMsgQueue, IPC_STAT, &msgInfo ) )	{		printf( "不能读取消息队列的属性" );		exit( 1 );	}	for ( counter = 0; counter < msgInfo.msg_qnum; counter++ )		msgrcv( nMsgQueue, &ReceiveMsg, sizeof( ReceiveMsg.Data ), MEMORY_INFO, IPC_NOWAIT );				// 启动两个线程来监控内存的变化	pthread_mutex_init( &mutexMap, NULL );		// 从消息队列中读取消息并进行分析	pthread_create( &tid1, NULL, AnalyseMessage, NULL );	// 显示分析的结果	pthread_create( &tid2, NULL, DisplayStatResult, NULL );	// 监控键盘输入	pthread_create( &tid3, NULL, MonitorKeyboard, NULL );		pthread_join( tid1, NULL );	pthread_join( tid2, NULL );	pthread_join( tid3, NULL );		endwin();}void*AnalyseMessage( void *arg ){	MsgBuffer ReceiveMsg;		while( 1 )	{		// get the message from the message queue		int reval = msgrcv( nMsgQueue, &ReceiveMsg, sizeof( ReceiveMsg.Data ), MEMORY_INFO, 0 );		if ( reval != -1 )		{			map<void *, StatResult>::iterator record;			record = mapMemory.find( ReceiveMsg.Data.pBuffer );			if ( record != mapMemory.end() ) // find it in the map			{				pthread_mutex_lock( &mutexMap );				if ( ARRAY_NEW == ReceiveMsg.Data.OperationType ||					SINGLE_NEW == ReceiveMsg.Data.OperationType )				{					// 这个情况相当于使用new/new[]后, 没有删除					// 又分配了一块内存.					// 新分配的内存的指针应该与以前的指针不相同的					// 如果相同则表示有一个delete消息被丢失了或传送的					// 消息有误					totalLeak += ReceiveMsg.Data.AllocSize;										record->second.TotalSize += 						ReceiveMsg.Data.AllocSize;					record->second.OperationType = 						ReceiveMsg.Data.OperationType;					// 这个应该是不会出现的,如果出现了应该仔细地分析原因					// 这个情况在被测进程使用了fork创建子进程时会出现的					char prompt[ 128 ];					sprintf( prompt, "致命错误(%s:%d), 请仔细检查你的代码", __FILE__, __LINE__ );										pthread_cancel( tid2 );					pthread_cancel( tid3 );					popWin( prompt );					endwin();					exit( 1 );				}				else if ( SINGLE_DELETE == ReceiveMsg.Data.OperationType ||					ARRAY_DELETE == ReceiveMsg.Data.OperationType )				{					/**************仔细检查以下这段代码**************/					// alloc with new[] but free with delete, but not delete[]					if ( SINGLE_DELETE == ReceiveMsg.Data.OperationType &&						ARRAY_NEW == record->second.OperationType )					{						list<StatResult>::iterator ite;												for ( ite = listMemory.begin(); ite != listMemory.end(); ++ite )						{							if ( 0 == strcasecmp( ite->Filename, record->second.Filename )								&& ite->LineNum == record->second.LineNum )							{								ite->TotalSize += record->second.TotalSize;								break;							}						}						if ( ite == listMemory.end() )							listMemory.push_back( record->second );											}					else						totalLeak -= record->second.TotalSize;										mapMemory.erase( record );					/**************仔细检查以上这段代码**************/				}				else // unknown memory operation, ignore now.				{					char prompt[ 128 ];										sprintf( prompt, "致命错误(%s:%d), 请仔细检查你的代码", __FILE__, __LINE__ );					pthread_cancel( tid2 );					pthread_cancel( tid3 );					popWin( prompt );					endwin();					exit( 1 );				}				pthread_mutex_unlock( &mutexMap );			}			else			{				if ( SINGLE_NEW == ReceiveMsg.Data.OperationType || 					ARRAY_NEW == ReceiveMsg.Data.OperationType )				{					StatResult result;										totalLeak += ReceiveMsg.Data.AllocSize;										strncpy( result.Filename, ReceiveMsg.Data.Filename, FILENAME_LENGTH - 1 );					result.Filename[ FILENAME_LENGTH - 1 ]= '\0';					result.LineNum = ReceiveMsg.Data.LineNum;					result.MemoryPtr = ReceiveMsg.Data.pBuffer;					result.OperationType = ReceiveMsg.Data.OperationType;					result.TotalSize = ReceiveMsg.Data.AllocSize;					pair<void *, StatResult> value( ReceiveMsg.Data.pBuffer, result );					mapMemory.insert( value );				}				else				{									}			}		}		else		{			if (( EIDRM == errno )||(EINVAL == errno)) // the message queue has removed, exit the thread			{				char *prompt = "消息队列已经被删除, 退出内存快照.";				pthread_cancel( tid2 );				pthread_cancel( tid3 );								popWin( prompt );				endwin();				break;			}			}	}	return NULL;}void DecodeErr( int ErrNum ){	switch( ErrNum )	{	case EAGAIN:		printf( "---EAGAIN---\n" );		break;	case EACCES:		printf( "---EACCES---\n" );		break;	case EFAULT:		printf( "---EFAULT---\n" );		break;	case EIDRM:		printf( "---EIDRM---\n" );		break;	case EINTR:		printf( "---EINTR---\n" );		break;	case EINVAL:		printf( "---EINVAL---\n" );		break;	case ENOMEM:		printf( "---ENOMEM---\n" );		break;	default:		printf( "---Undefined---\n" );		break;		}}/* ############################################################################ 显示内存泄漏的结果, 目前是一秒钟显示一次. 目前的DEBUG: . 屏幕刷新不对 ############################################################################*/void *DisplayStatResult(  void *arg ){	char content[128];	int line  = 0;	unsigned long total;	list<StatResult> temp;	static bool firstFlag = true;		temp.clear();		while( true )	{		pthread_mutex_lock( &mutexMap );		map<void*, StatResult>::iterator record1;		list<StatResult>::iterator record2;						// 统计mapMemory中的泄漏记录		// 注意这里是++record1而不是record1++		for ( record1 = mapMemory.begin(); record1 != mapMemory.end(); ++record1 )		{			list<StatResult>::iterator ite;									for ( ite = temp.begin(); ite != temp.end(); ++ite )			{				if ( 0 == strcasecmp( ite->Filename, record1->second.Filename )					&& ite->LineNum == record1->second.LineNum )				{					ite->TotalSize += record1->second.TotalSize;					break;				}			}			if ( ite == temp.end() )			{				StatResult tmpRecord;				memcpy( &tmpRecord, &(record1->second), sizeof( tmpRecord ) );				// 不能写成push_back( record1->second )				temp.push_back( tmpRecord );			}		}		// 统计listMemory中的泄漏记录		// 注意这里是++record2而不是record2++		for ( record2 = listMemory.begin(); record2 != listMemory.end(); ++record2 )		{			list<StatResult>::iterator ite;			// 同样这里是++ite而不是ite++			for ( ite = temp.begin(); ite != temp.end(); ++ite )			{				if ( 0 == strcasecmp( ite->Filename, record2->Filename )					&& ite->LineNum == record2->LineNum )				{					ite->TotalSize += record2->TotalSize;					break;				}			}			if ( ite == temp.end() )			{				// 注意这里不能直接写成temp.push_back( *record2 );				StatResult tmpRecord;				memcpy( &tmpRecord, &(*record2), sizeof( tmpRecord ) );				temp.push_back( tmpRecord );			}		}							if ( 0 != totalLeak )		{			erase(); // clear the screen						mvprintw( line++, 0, "被检测的进程是 ID = %d, 目前 %d 秒进行一次统计", pid, interval );			mvprintw( line++, 0, "" );					mvprintw( line++, 0, "" );						attrset( A_REVERSE );			mvprintw( line++, 0, "%-24s%-12s%-22s%-8s\n",				"所在文件", "行号", "分配内存数量(字节)", "所占比例" );			attrset( A_NORMAL );			refresh();						// 注意这里也不要写成record2++			for ( record2 = temp.begin(); record2 != temp.end(); ++record2 )			{				mvprintw( line++, 0, "%-24s%-12d%-22d%2.2f%%",					record2->Filename, record2->LineNum,					record2->TotalSize,					( double ) record2->TotalSize * 100 / totalLeak );			}			mvprintw( line++, 0, "");			mvprintw( line++, 0, "内存分配统计, 共 %d 字节(%0.1fKB = %0.1fMB)",				totalLeak, ( double )totalLeak / 1024, ( double ) totalLeak / 1024 / 1024 );			refresh(); // output to screen			line = 0;		}		else		{					}		temp.clear();		pthread_mutex_unlock( &mutexMap );		sleep( interval );	}	return NULL;}void *MonitorKeyboard(  void *arg ){	int ch;		for(;;)	{		ch = getch();		switch( ch )		{		case 'q':		case 'Q':			endwin();			exit( 0 );		case 'i':		case 'I':			if ( interval >= 30 )				interval = 30;			else				interval++;			break;		case 'd':		case 'D':			if ( interval <= 1 )				interval = 1;			else				interval--;			break;		default:			break;			}	}}voidinitial(){	initscr();	cbreak();	noecho();	nonl();			mvprintw( 0, 0, "被检测的进程是 ID = %d, 目前 %d 秒进行一次统计", pid, interval );	attrset( A_REVERSE );		mvprintw( 3, 0, "%-24s%-12s%-22s%-8s\n",		"所在文件", "行号", "分配内存数量(字节)", "所占比例" );	attrset( A_NORMAL );	refresh();}voidpopWin( char *str ){	char *prompt = "按任何键退出";	WINDOW *popWin = newwin( 4, 70, LINES/2 - 3, COLS / 2 - 35 ); 		box( popWin, '#' , '#' ); 	if ( str )		mvwaddstr( popWin, 1, ( 70 - strlen( str ) ) / 2,  str );	mvwaddstr( popWin, 2, ( 70 - strlen( prompt ) ) / 2,  prompt );	touchwin( popWin );	wrefresh( popWin );	getch();	touchwin( stdscr );	refresh();}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -