/**************************************************************************** * * * Project 64 - A Nintendo 64 emulator. * * http://www.pj64-emu.com/ * * Copyright (C) 2012 Project64. All rights reserved. * * * * License: * * GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html * * * ****************************************************************************/ #include "stdafx.h" enum { MAX_FRAMES = 13 }; CProfiling::CProfiling () : m_CurrentTimerAddr(Timer_None), m_CurrentDisplayCount(MAX_FRAMES), m_StartTimeHi(0), m_StartTimeLo(0) { } SPECIAL_TIMERS CProfiling::StartTimer(SPECIAL_TIMERS Address) { SPECIAL_TIMERS OldTimerAddr = StopTimer(); m_CurrentTimerAddr = Address; DWORD HiValue, LoValue; _asm { pushad rdtsc mov HiValue, edx mov LoValue, eax popad } m_StartTimeHi = HiValue; m_StartTimeLo = LoValue; return OldTimerAddr; } SPECIAL_TIMERS CProfiling::StopTimer(void) { DWORD HiValue, LoValue; if (m_CurrentTimerAddr == Timer_None) { return m_CurrentTimerAddr; } _asm { pushad rdtsc mov HiValue, edx mov LoValue, eax popad } __int64 StopTime = ((unsigned __int64)HiValue << 32) + (unsigned __int64)LoValue; __int64 StartTime = ((unsigned __int64)m_StartTimeHi << 32) + (unsigned __int64)m_StartTimeLo; __int64 TimeTaken = StopTime - StartTime; PROFILE_ENRTY Entry = m_Entries.find(m_CurrentTimerAddr); if (Entry != m_Entries.end()) { Entry->second += TimeTaken; } else { m_Entries.insert(PROFILE_ENRTIES::value_type(m_CurrentTimerAddr,TimeTaken)); } SPECIAL_TIMERS OldTimerAddr = m_CurrentTimerAddr; m_CurrentTimerAddr = Timer_None; return OldTimerAddr; } void CProfiling::ShowCPU_Usage (void) { __int64 TotalTime, CPU = 0, Alist = 0, Dlist = 0, Idle = 0; PROFILE_ENRTY Entry; if (m_CurrentDisplayCount > 0) { m_CurrentDisplayCount -= 1; return; } m_CurrentDisplayCount = MAX_FRAMES; Entry = m_Entries.find(Timer_R4300); if (Entry != m_Entries.end()) { CPU = Entry->second; } Entry = m_Entries.find(Timer_RefreshScreen); if (Entry != m_Entries.end()) { CPU += Entry->second; } Entry = m_Entries.find(Timer_RSP_Dlist); if (Entry != m_Entries.end()) { Dlist = Entry->second; } Entry = m_Entries.find(Timer_UpdateScreen); if (Entry != m_Entries.end()) { Dlist += Entry->second; } Entry = m_Entries.find(Timer_RSP_Alist); if (Entry != m_Entries.end()) { Alist = Entry->second; } Entry = m_Entries.find(Timer_Idel); if (Entry != m_Entries.end()) { Idle = Entry->second; } TotalTime = CPU + Alist + Dlist + Idle; g_Notify->DisplayMessage(0,L"r4300i: %0.1f%c GFX: %0.1f%c Alist: %0.1f%c Idle: %0.1f%c", (float)(((double)CPU / (double)TotalTime) * 100),'%', (float)(((double)Dlist / (double)TotalTime) * 100),'%', (float)(((double)Alist / (double)TotalTime) * 100),'%', (float)(((double)Idle / (double)TotalTime) * 100),'%'); ResetCounters(); } void CProfiling::ResetCounters (void) { m_Entries.clear(); } typedef struct { SPECIAL_TIMERS Timer; char * Name; } TIMER_NAME; void CProfiling::GenerateLog(void) { stdstr LogFileName; { CLog Log; Log.Open("Profiling.txt"); LogFileName = Log.FileName(); //Get the total time __int64 TotalTime = 0; for (PROFILE_ENRTY itemTime = m_Entries.begin(); itemTime != m_Entries.end(); itemTime++ ) { TotalTime += itemTime->second; } //Create a sortable list of items std::vector ItemList; for (PROFILE_ENRTY Entry = m_Entries.begin(); Entry != m_Entries.end(); Entry++ ) { ItemList.push_back(&(*Entry)); } //sort the list with a basic bubble sort for (size_t OuterPass = 0; OuterPass < (ItemList.size() - 1); OuterPass++ ) { for (size_t InnerPass = 0; InnerPass < (ItemList.size() - 1); InnerPass++ ) { if (ItemList[InnerPass]->second < ItemList[InnerPass + 1]->second) { PROFILE_VALUE * TempPtr = ItemList[InnerPass]; ItemList[InnerPass] = ItemList[InnerPass + 1]; ItemList[InnerPass + 1] = TempPtr; } } } TIMER_NAME TimerNames[] = { {Timer_R4300, "R4300"}, {Timer_RSP_Dlist, "RSP: Dlist"}, {Timer_RSP_Alist, "RSP: Alist"}, {Timer_RSP_Unknown, "RSP: Unknown"}, {Timer_RefreshScreen, "Refresh Screen"}, {Timer_UpdateScreen, "Update Screen"}, {Timer_UpdateFPS, "Update FPS"}, {Timer_FuncLookup, "Function Lookup"}, {Timer_Done, "Timer_Done"}, {Timer_GetBlockInfo, "Timer_GetBlockInfo"}, {Timer_AnalyseBlock, "Timer_AnalyseBlock"}, {Timer_CompileBlock, "Timer_CompileBlock"}, {Timer_CompileDone, "Timer_CompileDone"}, }; for (size_t count =0; count < ItemList.size(); count++ ) { char Buffer[255]; double CpuUsage = ((double)ItemList[count]->second / (double)TotalTime) * 100; if (CpuUsage <= 0.2) { continue; } sprintf(Buffer,"Func 0x%08X",ItemList[count]->first); for (int NameID = 0; NameID < (sizeof(TimerNames) / sizeof(TIMER_NAME)); NameID++) { if (ItemList[count]->first == TimerNames[NameID].Timer) { strcpy(Buffer,TimerNames[NameID].Name); break; } } Log.LogF("%s\t%2.2f",Buffer, CpuUsage); } } ShellExecute(NULL,"open",LogFileName.c_str(),NULL,NULL,SW_SHOW); ResetCounters(); }