追踪多线程调用路径关系

 在阅读多线程程序时,最头疼的就是不知道那些程序运行在同一个线程中。下面的程序可以方便的加入到代码中,并且记录下各个线程调用路径:

 .h file:

#ifndef UTILITY_H_HZ_FW
#define UTILITY_H_HZ_FW

#include <Windows.h>
#include <windef.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string>

#include <map>
#include <list>
#include <vector>

using namespace std;

// my definition
//===============
enum eOPER {REPLACE, DONOTHING, PUSHBACK};

// facility class to use critical section as lock. 
// critical section must be initialized before using this class
class CSLock {
public:
	CSLock(CRITICAL_SECTION &cs) : _cs(cs){
		EnterCriticalSection(&_cs);
	}
	~CSLock() {
		LeaveCriticalSection(&_cs);
	}
private:
	CRITICAL_SECTION &_cs;
};

class GetTimeStamp {
public:
	GetTimeStamp()
	{
		m_dwp = SetThreadAffinityMask(GetCurrentThread(), 0x1);
		if (m_dwp == 0)
		{
			cout << "set thread affinity failed: " << GetLastError() << endl;
		}
	}
	~GetTimeStamp()
	{
		if (m_dwp != 0)
			SetThreadAffinityMask(GetCurrentThread(), m_dwp);
	}
	string TimeStamp()
	{
		LARGE_INTEGER liPerfCount;
		QueryPerformanceCounter(&liPerfCount);

		LARGE_INTEGER liPerfFreq;
		QueryPerformanceFrequency(&liPerfFreq);

		__int64 iMs = liPerfCount.QuadPart * 1000 / liPerfFreq.QuadPart;
		char buf[11] = {0};
		sprintf(buf, "%I64d", iMs);
		return buf;
	}
private:
	DWORD_PTR m_dwp;
};

class FileWriter
{
public:
	FileWriter(const string& strFileName)
		: m_ofs(strFileName)
	{
	}
	~FileWriter()
	{
		m_ofs.close();
	}
	void WriteMessage(const string& strMessage, bool bAddTimestamp = true)
	{
		if (bAddTimestamp)
			m_ofs.write(m_timestamp.TimeStamp().append("-").c_str(), m_timestamp.TimeStamp().length()+1);
		m_ofs.write(strMessage.c_str(), strMessage.length());
		m_ofs.write("\n", 1);
		m_ofs.flush();
	}
	void WriteMessageInLine(const string& strMessage, bool bAddTimestamp = true)
	{
		if (bAddTimestamp)
			m_ofs.write(m_timestamp.TimeStamp().append("-").c_str(), m_timestamp.TimeStamp().length()+1);
		m_ofs.write(strMessage.c_str(), strMessage.length());
		m_ofs.flush();
	}
private:
	ofstream m_ofs;
	GetTimeStamp m_timestamp;
};

// singleton class to record call chain for multiple threads
// data structure: 
//	thread 0: chain_1 (func1 -> func2 -> ... )
//			  chain_2 (func1 -> func2 -> ... )
//			  ...

//	thread 1: chain_1 (func1 -> func2 -> ... )
//			  chain_2 (func2 -> func2 -> ... )
//			  ...
//	...
class CallChainWithThread
{
public:
	// get this singleton class' instance
	static CallChainWithThread* Instance() {
		if (_pInst == 0) {
			_pInst = new CallChainWithThread();
		}

		return _pInst;
	}

	// add a call to a thread's call chain
	void AddACall(DWORD aDWThreadID, string aStrFuncName){
		CSLock csLock(_cs);
		_mapActiveRecords[aDWThreadID].push_back(aStrFuncName);
		if (UpdateRecord(aDWThreadID, _mapActiveRecords[aDWThreadID]) != DONOTHING)
			FlushFinalRecord();
	}

	// remove a cal from a thread's call chain
	void DelCall(DWORD aDWThreadID, string aStrFuncName=""){
		CSLock csLock(_cs);
		_mapActiveRecords[aDWThreadID].pop_back();

		// no need to update the final record, as we are only interested the longer record in the final record 
	}

protected:
	CallChainWithThread() :
		 _fw("houzhe_thread_recrod.txt")
	{
		InitializeCriticalSection(&_cs);
	}

	~CallChainWithThread()
	{
		// FlushFinalRecord();
		DeleteCriticalSection(&_cs);
	}
private:
	// private methods
	//=================

	// update record according to a new call chain of the thread
	eOPER UpdateRecord(DWORD aDWThreadID, const list<string> & aLstRecord)
	{
		if (_mapRecords.find(aDWThreadID) == _mapRecords.end()) {
			// if this is the first call chain of the thread, we just push it back
			_mapRecords[aDWThreadID].push_back(aLstRecord);
			return DONOTHING;
		}

		// if an existing record is only part of the new one, we need replace it; 
		// if an existing record is same or is longer than the new one, we need do nothing
		// otherwise, we just push it back.
		eOPER opera = PUSHBACK;

		vector< list<string> >::iterator iter = _mapRecords[aDWThreadID].begin();
		while (iter != _mapRecords[aDWThreadID].end()) {
			list<string>::const_iterator newRecIter = aLstRecord.begin();
			list<string>::const_iterator oldRecIter = (*iter).begin();
			while ((newRecIter != aLstRecord.end()) && (oldRecIter != (*iter).end()) && (*newRecIter == *oldRecIter)) {
				newRecIter++;
				oldRecIter++;
			}

			if ((newRecIter != aLstRecord.end()) && (oldRecIter == (*iter).end())) {
				// _mapRecords[aDWThreadID].erase(iter);
				// _mapRecords[aDWThreadID].push_back(aLstRecord);
				// return;
				opera = REPLACE;
				break;
			}
			
			if (newRecIter == aLstRecord.end()) {
				opera = DONOTHING;
				break;
			}
			iter++;
		}
		if (opera == REPLACE) {
			_mapRecords[aDWThreadID].erase(iter);
			_mapRecords[aDWThreadID].push_back(aLstRecord);
		}
		else if (opera == PUSHBACK) {
			_mapRecords[aDWThreadID].push_back(aLstRecord);
		}
		return opera;
	}

	// flush the final records to file
	void FlushFinalRecord() {
		map<DWORD, vector< list<string> > >::iterator iter = _mapRecords.begin();
		while (iter != _mapRecords.end()){
			char buf[100] = {0};
			sprintf(buf, "Thread %u:", iter->first);
			_fw.WriteMessage(buf);
			vector< list<string> >::iterator vecIter = iter->second.begin();
			while (vecIter != iter->second.end()) {
				list<string>::iterator lstIter = vecIter->begin();
				while (lstIter != vecIter->end()) {
					_fw.WriteMessageInLine(*lstIter, false);
					_fw.WriteMessageInLine(" -> ", false);
					lstIter++;
				}
				_fw.WriteMessageInLine("\n", false);
				vecIter++;
			}
			iter++;
		}

		_fw.WriteMessageInLine("=============================\n", false);
		_fw.WriteMessageInLine("\n", false);
	}

	// private members
	//================

	static CallChainWithThread *_pInst;					// this singleton class' instance
	map<DWORD, vector< list<string> > > _mapRecords;	// records of all threads' call chain
	map<DWORD, list<string> > _mapActiveRecords;		// active call chain of all threads

	FileWriter _fw;										// file to write records in 
	CRITICAL_SECTION _cs;								// protect _mapRecords and _mapActiveRecords
};

// Facility class to use CallChainWithThread. Initialize this class
// at the beginning of every function to record its call chain with thread information.
class FileInfoWithThread
{
public:
	FileInfoWithThread() {

	}
	FileInfoWithThread(const string& funcName){
		CallChainWithThread::Instance()->AddACall(GetCurrentThreadId(), funcName);
	}

	~FileInfoWithThread(){
		CallChainWithThread::Instance()->DelCall(GetCurrentThreadId());
	}

};

#endif

.cpp file

#include "houzhe_utility_filewriter.h"

CallChainWithThread *CallChainWithThread::_pInst = 0;

使用:

void f()
{
    FileInfoWithThread fiwt(__FUNCTION__);
}

输出举例:

27504647-Thread 8808:
RTCPThreadRun -> SetSocketList -> 
27504647-Thread 9324:
initScpHandler -> initScpHandlerInternal -> setVtbl -> 
registNotificationCallback -> registNotificationCallbackInternal -> findCallbackInList -> 
InitRtcpHandler -> InitSocket -> 
startRTCPDaemonThread -> 



刚刚发现这里实际有两个bug:

1. 因为singleton是new出来的,所以在程序退出时它的析构函数并没有被调用。建议放一个全局变量并在该变量的析构函数中调用CallChainWithThread的FlushFinalRecord()。

2. 是一个设计问题。因为所有的追踪是基于thread id的,考虑有些程序经常创建和销毁线程,如果thread id被重复生成,追踪的结果就不准确了。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值