/*---------------------------------------------------------------------------* Project: NintendoWare File: dev_Profile.cpp Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Revision: 22001 $ *---------------------------------------------------------------------------*/ #include #include #include #ifdef NW_PLATFORM_CTR //#define PERFORMANCE_COUNTER_ENABLED #endif #ifdef PERFORMANCE_COUNTER_ENABLED #include #include #endif #ifndef NW_PLATFORM_CTR #include #endif #ifdef NW_DEV_ENABLED namespace nw { namespace dev { ProfileCenter* ProfileCenter::m_Instance = NULL; //---------------------------------------- ProfileManager* ProfileManager::CreateProfileManager( ProfileManager::Description& description, os::IAllocator* allocator) { ut::MoveArray reports(description.maxReport, allocator); void* managerMemory = allocator->Alloc(sizeof(ProfileManager)); ProfileManager* profileManager = new(managerMemory) ProfileManager(allocator, description, reports); return profileManager; } //---------------------------------------- void ProfileManager::Destroy(os::IAllocator* allocator) { this->~ProfileManager(); allocator->Free( this ); } //---------------------------------------- ProfileManager::ProfileManager( os::IAllocator* allocator, const ProfileManager::Description& description, ut::MoveArray reports) : m_Description(description), m_Reports(reports) { NW_UNUSED_VARIABLE(allocator); } //---------------------------------------- ProfileManager::~ProfileManager() { } //---------------------------------------- bool ProfileManager::AddReport(const Report& report) { return this->m_Reports.push_back(report); } //---------------------------------------- bool ProfileManager::StoreReport(const Report& report) { ReportRange range = this->GetReports(); // 同一の名前が設定されたレポートを1つにまとめながら集計する。 for(ReportArray::iterator it = range.first; it != range.second; ++it) { if (::std::strcmp(it->name, report.name) == 0) { it->callCount += report.callCount; it->elapsedTime += report.elapsedTime; it->iCacheMiss += report.iCacheMiss; it->dCacheReadMiss += report.dCacheReadMiss; it->dCacheWriteMiss += report.dCacheWriteMiss; if (it->maxElapsedTime < report.maxElapsedTime) { it->maxElapsedTime = report.maxElapsedTime; } if (it->minElapsedTime > report.minElapsedTime) { it->minElapsedTime = report.minElapsedTime; } return true; } } return this->m_Reports.push_back(report); } //---------------------------------------- void ProfileManager::ClearReports() { m_Reports.clear(); } //---------------------------------------- void ProfileManager::DumpReports() { NW_DEV_LOG("\n"); float accuracy = 0.0f; const int MAX_LENGTH = 256; switch (this->m_Description.accuracy) { case MILI_SECOND: { accuracy = 1000000.0f; NW_DEV_LOG("Time: [ms]\n"); } break; case MICRO_SECOND: { accuracy = 1000.0f; NW_DEV_LOG("Time: [us]\n"); } break; default: { NW_FATAL_ERROR("Unsupported accuracy."); } break; } char caption[MAX_LENGTH]; caption[0] = '\0'; if (ut::CheckFlag(m_Description.mode, ProfileManager::TIMER)) { char* str = "| total| call|average|average%%| max| min|"; ut::strncat(caption, MAX_LENGTH, str, std::strlen(str) + 1); } #ifdef PERFORMANCE_COUNTER_ENABLED if (ut::CheckFlag(m_Description.mode, ProfileManager::I_CACHE_MISS)) { char* str = "IMiss|"; ut::strncat(caption, MAX_LENGTH, str, std::strlen(str) + 1); } if (ut::CheckFlag(m_Description.mode, ProfileManager::D_CACHE_READ_MISS)) { char* str = "DRMiss|"; ut::strncat(caption, MAX_LENGTH, str, std::strlen(str) + 1); } if (ut::CheckFlag(m_Description.mode, ProfileManager::D_CACHE_WRITE_MISS)) { char* str = "DWMiss|"; ut::strncat(caption, MAX_LENGTH, str, std::strlen(str) + 1); } #endif if (this->m_Description.exportFormat == CSV) { std::replace(caption, caption + std::strlen(caption), '|', ','); char* str = "name,\n"; ut::strncat(caption, MAX_LENGTH, str, std::strlen(str) + 1); } else { char* str = "name|h\n"; ut::strncat(caption, MAX_LENGTH, str, std::strlen(str) + 1); } // caption 出力 NW_DEV_LOG(caption); ProfileManager::ReportRange range = this->GetReports(); for(ReportArray::iterator it = range.first; it != range.second; ++it) { float elapsedTime = TickToTime(it->elapsedTime, accuracy); float maxElapsedTime = TickToTime(it->maxElapsedTime, accuracy); float minElapsedTime = TickToTime(it->minElapsedTime, accuracy); float averageTime = elapsedTime / static_cast(it->callCount); float frame = 10.0f / 60.0f; u64 iCacheMiss = (it->iCacheMiss / it->callCount); u64 dCacheReadMiss = (it->dCacheReadMiss / it->callCount); u64 dCacheWriteMiss = (it->dCacheWriteMiss / it->callCount); char formatString[MAX_LENGTH]; formatString[0] = '\0'; char output[256]; output[0] = '\0'; if (ut::CheckFlag(m_Description.mode, ProfileManager::TIMER)) { char* format = "|%9.3f|%5d|%7.3f| %5.2f|%7.3f|%7.3f|"; ut::snprintf(formatString, MAX_LENGTH, MAX_LENGTH - 1, format, elapsedTime, it->callCount, averageTime, averageTime / frame, maxElapsedTime, minElapsedTime); ut::strncat(output, MAX_LENGTH, formatString, std::strlen(formatString) + 1); } #ifdef PERFORMANCE_COUNTER_ENABLED if (ut::CheckFlag(m_Description.mode, ProfileManager::I_CACHE_MISS)) { char* format = "%5llu|"; ut::snprintf(formatString, MAX_LENGTH, MAX_LENGTH - 1, format, iCacheMiss); ut::strncat(output, MAX_LENGTH, formatString, std::strlen(formatString) + 1); } if (ut::CheckFlag(m_Description.mode, ProfileManager::D_CACHE_READ_MISS)) { char* format = "%6llu|"; ut::snprintf(formatString, MAX_LENGTH, MAX_LENGTH - 1, format, dCacheReadMiss); ut::strncat(output, MAX_LENGTH, formatString, std::strlen(formatString) + 1); } if (ut::CheckFlag(m_Description.mode, ProfileManager::D_CACHE_WRITE_MISS)) { char* format = "%6llu|"; ut::snprintf(formatString, MAX_LENGTH, MAX_LENGTH - 1, format, dCacheWriteMiss); ut::strncat(output, MAX_LENGTH, formatString, std::strlen(formatString) + 1); } #endif ut::strncat(output, MAX_LENGTH, it->name, std::strlen(it->name) + 1); char* str = "|\n"; ut::strncat(output, MAX_LENGTH, str, std::strlen(str) + 1); if (this->m_Description.exportFormat == CSV) { std::replace(output, output + std::strlen(output), '|', ','); } NW_DEV_LOG(output); } } //---------------------------------------- AutoProfile::AutoProfile(const char* name, ProfileManager* profileManager) : m_ProfileManager(profileManager), m_Time(0), m_ICacheMiss(0), m_DCacheReadMiss(0), m_DCacheWriteMiss(0) { m_Report.name = name; #ifdef PERFORMANCE_COUNTER_ENABLED const nn::dbg::MPCore::PerformanceCounterName CORE_0 = nn::dbg::MPCore::PERFORMANCE_COUNTER_NAME_CORE_0; const nn::dbg::MPCore::PerformanceCounterName CORE_1 = nn::dbg::MPCore::PERFORMANCE_COUNTER_NAME_CORE_1; const nn::dbg::MPCore::PerformanceCounterEvent INST_CACHE_MISS = nn::dbg::MPCore::PERFORMANCE_COUNTER_EVENT_CORE_INST_CACHE_MISS; const nn::dbg::MPCore::PerformanceCounterEvent DATA_CACHE_READ_MISS = nn::dbg::MPCore::PERFORMANCE_COUNTER_EVENT_CORE_DATA_CACHE_READ_MISS; const nn::dbg::MPCore::PerformanceCounterEvent DATA_CACHE_WRITE_MISS = nn::dbg::MPCore::PERFORMANCE_COUNTER_EVENT_CORE_DATA_CACHE_WRITE_MISS; if (ut::CheckFlag(profileManager->GetDescription().mode, ProfileManager::I_CACHE_MISS)) { nn::dbg::MPCore::SetPMCEvent(CORE_0, INST_CACHE_MISS); m_ICacheMiss = nn::dbg::MPCore::GetPMCValue(CORE_0); if (ut::CheckFlag(this->m_ProfileManager->GetDescription().mode, ProfileManager::D_CACHE_READ_MISS)) { nn::dbg::MPCore::SetPMCEvent(CORE_1, DATA_CACHE_READ_MISS); m_DCacheReadMiss = nn::dbg::MPCore::GetPMCValue(CORE_1); } else if (ut::CheckFlag(this->m_ProfileManager->GetDescription().mode, ProfileManager::D_CACHE_WRITE_MISS)) { nn::dbg::MPCore::SetPMCEvent(CORE_1, DATA_CACHE_WRITE_MISS); m_DCacheWriteMiss = nn::dbg::MPCore::GetPMCValue(CORE_1); } } else { if (ut::CheckFlag(this->m_ProfileManager->GetDescription().mode, ProfileManager::D_CACHE_READ_MISS)) { nn::dbg::MPCore::SetPMCEvent(CORE_0, DATA_CACHE_READ_MISS); m_DCacheReadMiss = nn::dbg::MPCore::GetPMCValue(CORE_0); } if (ut::CheckFlag(this->m_ProfileManager->GetDescription().mode, ProfileManager::D_CACHE_WRITE_MISS)) { nn::dbg::MPCore::SetPMCEvent(CORE_1, DATA_CACHE_WRITE_MISS); m_DCacheWriteMiss = nn::dbg::MPCore::GetPMCValue(CORE_1); } } #endif m_Time = this->GetTime(); } //---------------------------------------- AutoProfile::~AutoProfile() { m_Time = this->GetTime() - m_Time; #ifdef PERFORMANCE_COUNTER_ENABLED const nn::dbg::MPCore::PerformanceCounterName CORE_0 = nn::dbg::MPCore::PERFORMANCE_COUNTER_NAME_CORE_0; const nn::dbg::MPCore::PerformanceCounterName CORE_1 = nn::dbg::MPCore::PERFORMANCE_COUNTER_NAME_CORE_1; const nn::dbg::MPCore::PerformanceCounterEvent INST_CACHE_MISS = nn::dbg::MPCore::PERFORMANCE_COUNTER_EVENT_CORE_INST_CACHE_MISS; const nn::dbg::MPCore::PerformanceCounterEvent DATA_CACHE_READ_MISS = nn::dbg::MPCore::PERFORMANCE_COUNTER_EVENT_CORE_DATA_CACHE_READ_MISS; const nn::dbg::MPCore::PerformanceCounterEvent DATA_CACHE_WRITE_MISS = nn::dbg::MPCore::PERFORMANCE_COUNTER_EVENT_CORE_DATA_CACHE_WRITE_MISS; if (ut::CheckFlag(this->m_ProfileManager->GetDescription().mode, ProfileManager::I_CACHE_MISS)) { nn::dbg::MPCore::SetPMCEvent(CORE_0, INST_CACHE_MISS); m_ICacheMiss = nn::dbg::MPCore::GetPMCValue(CORE_0) - m_ICacheMiss; m_Report.iCacheMiss = m_ICacheMiss; if (ut::CheckFlag(this->m_ProfileManager->GetDescription().mode, ProfileManager::D_CACHE_READ_MISS)) { nn::dbg::MPCore::SetPMCEvent(CORE_1, DATA_CACHE_READ_MISS); m_DCacheReadMiss = nn::dbg::MPCore::GetPMCValue(CORE_1) - m_DCacheReadMiss; m_Report.dCacheReadMiss = m_DCacheReadMiss; } else if (ut::CheckFlag(this->m_ProfileManager->GetDescription().mode, ProfileManager::D_CACHE_WRITE_MISS)) { nn::dbg::MPCore::SetPMCEvent(CORE_1, DATA_CACHE_WRITE_MISS); m_DCacheWriteMiss = nn::dbg::MPCore::GetPMCValue(CORE_1) - m_DCacheWriteMiss; m_Report.dCacheWriteMiss = m_DCacheWriteMiss; } } else { if (ut::CheckFlag(this->m_ProfileManager->GetDescription().mode, ProfileManager::D_CACHE_READ_MISS)) { nn::dbg::MPCore::SetPMCEvent(CORE_0, DATA_CACHE_READ_MISS); m_DCacheReadMiss = nn::dbg::MPCore::GetPMCValue(CORE_0) - m_DCacheReadMiss; m_Report.dCacheReadMiss = m_DCacheReadMiss; } if (ut::CheckFlag(this->m_ProfileManager->GetDescription().mode, ProfileManager::D_CACHE_WRITE_MISS)) { nn::dbg::MPCore::SetPMCEvent(CORE_1, DATA_CACHE_WRITE_MISS); m_DCacheWriteMiss = nn::dbg::MPCore::GetPMCValue(CORE_1) - m_DCacheWriteMiss; m_Report.dCacheWriteMiss = m_DCacheWriteMiss; } } #endif m_Report.elapsedTime = m_Time; m_Report.callCount = 1; m_Report.maxElapsedTime = m_Time; m_Report.minElapsedTime = m_Time; m_ProfileManager->StoreReport(m_Report); } //---------------------------------------- s64 AutoProfile::GetTime() { #ifdef NW_PLATFORM_CTR return static_cast(nn::os::Tick::GetSystemCurrent()); #else timeBeginPeriod(1); return timeGetTime() * 1000000; timeEndPeriod(1); #endif } //---------------------------------------- void ProfileCenter::Initialize(int maxReport, os::IAllocator* allocator) { NW_ASSERT(m_Instance == NULL); #ifdef PERFORMANCE_COUNTER_ENABLED // プログラムカウンタ初期化 nn::dbg::MPCore::AcquirePMCControl(); #endif ProfileManager::Description description; description.maxReport = maxReport; description.exportFormat = ProfileManager::CHART; description.accuracy = ProfileManager::MILI_SECOND; description.mode = ProfileManager::TIMER | ProfileManager::I_CACHE_MISS | ProfileManager::D_CACHE_READ_MISS; ProfileManager* profileManager = ProfileManager::CreateProfileManager(description, (allocator)); void* memory = allocator->Alloc(sizeof(ProfileCenter)); NW_NULL_ASSERT(memory); m_Instance = new(memory) ProfileCenter(); ProfileCenter::SetProfileManager(profileManager); } //---------------------------------------- void ProfileCenter::Finalize(os::IAllocator* allocator) { ProfileManager* profileManager = ProfileCenter::GetProfileManager(); if (profileManager) { profileManager->Destroy(allocator); ProfileCenter::SetProfileManager(NULL); } os::SafeFree(m_Instance, allocator); #ifdef PERFORMANCE_COUNTER_ENABLED // プログラムカウンタ解放 nn::dbg::MPCore::ReleasePMCControl(); #endif } } // namespace nw::dev } // namespace nw #endif