追踪多线程调用路径关系

来源:互联网 发布:黑板报设计软件 编辑:程序博客网 时间:2024/05/16 18:14
 在阅读多线程程序时,最头疼的就是不知道那些程序运行在同一个线程中。下面的程序可以方便的加入到代码中,并且记录下各个线程调用路径:

 .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 classclass 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' instancestatic CallChainWithThread* Instance() {if (_pInst == 0) {_pInst = new CallChainWithThread();}return _pInst;}// add a call to a thread's call chainvoid 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 chainvoid 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 threadeOPER 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 filevoid 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' instancemap<DWORD, vector< list<string> > > _mapRecords;// records of all threads' call chainmap<DWORD, list<string> > _mapActiveRecords;// active call chain of all threadsFileWriter _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被重复生成,追踪的结果就不准确了。