mirror of
				https://github.com/dolphin-emu/dolphin.git
				synced 2025-10-25 01:19:19 +00:00 
			
		
		
		
	git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@4921 8ced0084-cf51-0410-be5f-012b33b47a6e
		
			
				
	
	
		
			298 lines
		
	
	
	
		
			8.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			298 lines
		
	
	
	
		
			8.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright (C) 2003 Dolphin Project.
 | |
| 
 | |
| // This program is free software: you can redistribute it and/or modify
 | |
| // it under the terms of the GNU General Public License as published by
 | |
| // the Free Software Foundation, version 2.0.
 | |
| 
 | |
| // This program is distributed in the hope that it will be useful,
 | |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| // GNU General Public License 2.0 for more details.
 | |
| 
 | |
| // A copy of the GPL 2.0 should have been included with the program.
 | |
| // If not, see http://www.gnu.org/licenses/
 | |
| 
 | |
| // Official SVN repository and contact information can be found at
 | |
| // http://code.google.com/p/dolphin-emu/
 | |
| 
 | |
| // Simple profiler
 | |
| 
 | |
| #include "Common.h"
 | |
| #include "Profiler.h"
 | |
| 
 | |
| #include <list>
 | |
| #include <string>
 | |
| #include <map>
 | |
| 
 | |
| #ifdef _WIN32
 | |
| 
 | |
| #if defined (_MSC_VER) && _MSC_VER >= 1400
 | |
| #define _interlockedbittestandset workaround_ms_header_bug_platform_sdk6_set
 | |
| #define _interlockedbittestandreset workaround_ms_header_bug_platform_sdk6_reset
 | |
| #define _interlockedbittestandset64 workaround_ms_header_bug_platform_sdk6_set64
 | |
| #define _interlockedbittestandreset64 workaround_ms_header_bug_platform_sdk6_reset64
 | |
| #include <intrin.h>
 | |
| #undef _interlockedbittestandset
 | |
| #undef _interlockedbittestandreset
 | |
| #undef _interlockedbittestandset64
 | |
| #undef _interlockedbittestandreset64
 | |
| #pragma intrinsic(__rdtsc)
 | |
| #endif
 | |
| 
 | |
| // Globals
 | |
| static u64 luPerfFreq = 0;
 | |
| #ifdef DVPROFILE
 | |
| int g_bWriteProfile = 1;
 | |
| #else
 | |
| int g_bWriteProfile = 1;
 | |
| #endif
 | |
| 
 | |
| inline u64 GET_PROFILE_TIME()
 | |
| {
 | |
| #if defined (_MSC_VER) && _MSC_VER >= 1400
 | |
|     return __rdtsc();
 | |
| #else
 | |
|     LARGE_INTEGER lu;
 | |
|     QueryPerformanceCounter(&lu);
 | |
|     return lu.QuadPart;
 | |
| #endif
 | |
| }
 | |
| #else
 | |
| static u64 luPerfFreq = 1000000;
 | |
| #define GET_PROFILE_TIME() //GetCpuTick()
 | |
| #endif
 | |
| 
 | |
| struct DVPROFSTRUCT;
 | |
| 
 | |
| struct DVPROFSTRUCT
 | |
| {
 | |
|     struct DATA
 | |
|     {
 | |
|         DATA(u64 time, u32 user = 0) : dwTime(time), dwUserData(user) {}
 | |
|         DATA() : dwTime(0), dwUserData(0) {}
 | |
| 
 | |
|         u64 dwTime;
 | |
|         u32 dwUserData;
 | |
|     };
 | |
| 
 | |
|     ~DVPROFSTRUCT() {
 | |
|         std::list<DVPROFSTRUCT *>::iterator it = listpChild.begin();
 | |
|         while (it != listpChild.end())
 | |
| 		{
 | |
|             delete *it;
 | |
| 			*it = NULL;
 | |
|             ++it;
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 	// before DVProfEnd is called, contains the global time it started
 | |
| 	// after DVProfEnd is called, contains the time it lasted
 | |
| 	// the list contains all the tracked times 
 | |
|     std::list<DATA> listTimes;
 | |
|                               
 | |
|     char pname[256];
 | |
|     std::list<DVPROFSTRUCT*> listpChild;     // other profilers called during this profiler period
 | |
| };
 | |
| 
 | |
| struct DVPROFTRACK
 | |
| {
 | |
|     u32 dwUserData;
 | |
|     DVPROFSTRUCT::DATA* pdwTime;
 | |
|     DVPROFSTRUCT* pprof;
 | |
| };
 | |
| 
 | |
| // the current profiling functions, the back element is the
 | |
| // one that will first get popped off the list when DVProfEnd is called
 | |
| // the pointer is an element in DVPROFSTRUCT::listTimes
 | |
| static std::list<DVPROFTRACK> g_listCurTracking;
 | |
| 
 | |
| // the current profilers, note that these are the parents
 | |
| // any profiler started during the time of another is held in
 | |
| // DVPROFSTRUCT::listpChild
 | |
| static std::list<DVPROFSTRUCT> g_listProfilers; 
 | |
| 
 | |
| // ignores the hierarchy, pointer to elements in g_listProfilers
 | |
| static std::list<DVPROFSTRUCT*> g_listAllProfilers;
 | |
| 
 | |
| 
 | |
| void DVProfRegister(const char *pname)
 | |
| {
 | |
|     if (!g_bWriteProfile)
 | |
|         return;
 | |
| 
 | |
| #ifdef _WIN32
 | |
|     if (luPerfFreq <= 1) {
 | |
| #if defined (_MSC_VER) && _MSC_VER >= 1400
 | |
|         luPerfFreq = 1000000;
 | |
| #else
 | |
|         LARGE_INTEGER temp;
 | |
|         QueryPerformanceFrequency(&temp);
 | |
|         luPerfFreq = temp.QuadPart;
 | |
| #endif
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     std::list<DVPROFSTRUCT*>::iterator it = g_listAllProfilers.begin();
 | |
|     
 | |
| //  while(it != g_listAllProfilers.end() ) {
 | |
| //
 | |
| //      if( _tcscmp(pname, (*it)->pname) == 0 ) {
 | |
| //          (*it)->listTimes.push_back(Common::Timer::GetTimeMs());
 | |
| //          DVPROFTRACK dvtrack;
 | |
| //          dvtrack.pdwTime = &(*it)->listTimes.back();
 | |
| //          dvtrack.pprof = *it;
 | |
| //          g_listCurTracking.push_back(dvtrack);
 | |
| //          return;
 | |
| //      }
 | |
| //
 | |
| //      ++it;
 | |
| //  }
 | |
| 
 | |
|     // else add in a new profiler to the appropriate parent profiler
 | |
|     DVPROFSTRUCT* pprof = NULL;
 | |
|     
 | |
|     if (g_listCurTracking.size() > 0)
 | |
| 	{
 | |
|         _assert_( g_listCurTracking.back().pprof != NULL );
 | |
|         g_listCurTracking.back().pprof->listpChild.push_back(new DVPROFSTRUCT());
 | |
|         pprof = g_listCurTracking.back().pprof->listpChild.back();
 | |
|     }
 | |
|     else
 | |
| 	{
 | |
|         g_listProfilers.push_back(DVPROFSTRUCT());
 | |
|         pprof = &g_listProfilers.back();
 | |
|     }
 | |
| 
 | |
|     strncpy(pprof->pname, pname, 256);
 | |
| 
 | |
|     // setup the profiler for tracking
 | |
|     pprof->listTimes.push_back(DVPROFSTRUCT::DATA(GET_PROFILE_TIME()));
 | |
| 
 | |
|     DVPROFTRACK dvtrack;
 | |
|     dvtrack.pdwTime = &pprof->listTimes.back();
 | |
|     dvtrack.pprof = pprof;
 | |
|     dvtrack.dwUserData = 0;
 | |
| 
 | |
|     g_listCurTracking.push_back(dvtrack);
 | |
| 
 | |
|     // add to all profiler list
 | |
|     g_listAllProfilers.push_back(pprof);
 | |
| }
 | |
| 
 | |
| void DVProfEnd(u32 dwUserData)
 | |
| {
 | |
|     if (!g_bWriteProfile)
 | |
|         return;
 | |
|     if (g_listCurTracking.size() == 0)
 | |
|         return;
 | |
| 
 | |
|     DVPROFTRACK dvtrack = g_listCurTracking.back();
 | |
| 
 | |
|     _assert_( dvtrack.pdwTime != NULL && dvtrack.pprof != NULL );
 | |
| 
 | |
|     dvtrack.pdwTime->dwTime = GET_PROFILE_TIME()- dvtrack.pdwTime->dwTime;
 | |
|     dvtrack.pdwTime->dwUserData= dwUserData;
 | |
| 
 | |
|     g_listCurTracking.pop_back();
 | |
| }
 | |
| 
 | |
| struct DVTIMEINFO
 | |
| {
 | |
|     DVTIMEINFO() : uInclusive(0), uExclusive(0) {}
 | |
|     u64 uInclusive, uExclusive;
 | |
| };
 | |
| 
 | |
| std::map<std::string, DVTIMEINFO> mapAggregateTimes;
 | |
| 
 | |
| u64 DVProfWriteStruct(FILE* f, const DVPROFSTRUCT* p, int ident)
 | |
| {
 | |
|     fprintf(f, "%*s%s - ", ident, "", p->pname);
 | |
|     std::list<DVPROFSTRUCT::DATA>::const_iterator ittime = p->listTimes.begin();
 | |
|     u64 utime = 0;
 | |
| 
 | |
|     while (ittime != p->listTimes.end())
 | |
| 	{
 | |
|         utime += ittime->dwTime;
 | |
|         if (ittime->dwUserData) 
 | |
|             fprintf(f, "time: %d, user: 0x%8.8x", (u32)ittime->dwTime, ittime->dwUserData);
 | |
|         else
 | |
|             fprintf(f, "time: %d", (u32)ittime->dwTime);
 | |
|         ++ittime;
 | |
|     }
 | |
| 
 | |
|     // yes this is necessary, maps have problems with constructors on their type
 | |
|     std::map<std::string, DVTIMEINFO>::iterator ittimes = mapAggregateTimes.find(p->pname);
 | |
|     if (ittimes == mapAggregateTimes.end())
 | |
| 	{
 | |
|         ittimes = mapAggregateTimes.insert(std::map<std::string, DVTIMEINFO>::value_type(p->pname, DVTIMEINFO())).first;
 | |
|         ittimes->second.uExclusive = 0;
 | |
|         ittimes->second.uInclusive = 0;
 | |
|     }
 | |
| 
 | |
|     ittimes->second.uInclusive += utime;
 | |
| 
 | |
|     fprintf(f, "\n");
 | |
| 
 | |
|     std::list<DVPROFSTRUCT*>::const_iterator itprof = p->listpChild.begin();
 | |
| 
 | |
|     u64 uex = utime;
 | |
|     while (itprof != p->listpChild.end())
 | |
| 	{
 | |
|         uex -= DVProfWriteStruct(f, *itprof, ident+4);
 | |
|         ++itprof;
 | |
|     }
 | |
| 
 | |
|     if (uex > utime)
 | |
|         uex = 0;
 | |
| 
 | |
|     ittimes->second.uExclusive += uex;
 | |
|     return utime;
 | |
| }
 | |
| 
 | |
| void DVProfWrite(const char* pfilename, u32 frames)
 | |
| {
 | |
|     _assert_( pfilename != NULL );
 | |
|     FILE* f = fopen(pfilename, "w");
 | |
| 
 | |
|     // pop back any unused
 | |
|     mapAggregateTimes.clear();
 | |
|     std::list<DVPROFSTRUCT>::iterator it = g_listProfilers.begin();
 | |
| 
 | |
|     while (it != g_listProfilers.end() )
 | |
| 	{
 | |
|         DVProfWriteStruct(f, &(*it), 0);
 | |
|         ++it;
 | |
|     }
 | |
| 
 | |
|     std::map<std::string, DVTIMEINFO>::const_iterator iter;
 | |
|     fprintf(f, "\n\n-------------------------------------------------------------------\n\n");
 | |
| 
 | |
|     u64 uTotal[2] = {0};
 | |
|     double fiTotalTime[2];
 | |
| 
 | |
|     for (iter = mapAggregateTimes.begin(); iter != mapAggregateTimes.end(); ++iter)
 | |
| 	{
 | |
|         uTotal[0] += iter->second.uExclusive;
 | |
|         uTotal[1] += iter->second.uInclusive;
 | |
|     }
 | |
| 
 | |
|     fprintf(f, "total times (%d): ex: %Lu ", frames, 1000000 * uTotal[0] / (luPerfFreq*(u64)frames));
 | |
|     fprintf(f, "inc: %Lu\n", 1000000 * uTotal[1]/(luPerfFreq*(u64)frames));
 | |
| 
 | |
|     fiTotalTime[0] = 1.0 / (double)uTotal[0];
 | |
|     fiTotalTime[1] = 1.0 / (double)uTotal[1];
 | |
| 
 | |
|     // output the combined times
 | |
|     for (iter = mapAggregateTimes.begin(); iter != mapAggregateTimes.end(); ++iter)
 | |
|         fprintf(f, "%s - ex: %f inc: %f\n", iter->first.c_str(), (float)((double)iter->second.uExclusive * fiTotalTime[0]),
 | |
|             (float)((double)iter->second.uInclusive * fiTotalTime[1]));
 | |
| 
 | |
|     fclose(f);
 | |
| }
 | |
| 
 | |
| void DVProfClear()
 | |
| {
 | |
|     g_listCurTracking.clear();
 | |
|     g_listProfilers.clear();
 | |
|     g_listAllProfilers.clear();
 | |
| }
 |