/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "StdAfx.h" #include "System.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for AZ_MAX_PATH_LEN #include #if defined(AZ_RESTRICTED_PLATFORM) #undef AZ_RESTRICTED_SECTION #define SYSTEMWIN32_CPP_SECTION_1 1 #define SYSTEMWIN32_CPP_SECTION_2 2 #define SYSTEMWIN32_CPP_SECTION_3 3 #endif #if defined(LINUX) || defined(APPLE) #include #endif #ifdef WIN32 #define WIN32_LEAN_AND_MEAN #include "windows.h" #include #include // Needed for ShellExecute. #include #include // HAX to resolve IEntity redefinition #define __IEntity_INTERFACE_DEFINED__ #include #endif #include "IDebugCallStack.h" #if defined(APPLE) || defined(LINUX) #include #endif #include "XConsole.h" #include "Statistics.h" #include "CrySizerStats.h" #include "CrySizerImpl.h" #include "StreamEngine/StreamEngine.h" #include "LocalizedStringManager.h" #include "XML/XmlUtils.h" #include "AutoDetectSpec.h" #include "CryPak.h" #pragma warning(disable: 4244) #if defined(WIN32) LINK_SYSTEM_LIBRARY(wininet.lib) LINK_SYSTEM_LIBRARY(Winmm.lib) #endif #if defined(APPLE) #include "SystemUtilsApple.h" #endif extern CMTSafeHeap* g_pPakHeap; // this is the list of modules that can be loaded into the game process // Each array element contains 2 strings: the name of the module (case-insensitive) // and the name of the group the module belongs to ////////////////////////////////////////////////////////////////////////// const char g_szGroupCore[] = "CryEngine"; const char* g_szModuleGroups[][2] = { {"Editor.exe", g_szGroupCore}, {"CrySystem.dll", g_szGroupCore}, {"CryNetwork.dll", g_szGroupCore}, {"CryPhysics.dll", g_szGroupCore}, {"CryFont.dll", g_szGroupCore}, {"Cry3DEngine.dll", g_szGroupCore}, {"CryRenderD3D9.dll", g_szGroupCore}, {"CryRenderD3D10.dll", g_szGroupCore}, {"CryRenderOGL.dll", g_szGroupCore}, {"CryRenderNULL.dll", g_szGroupCore} }; ////////////////////////////////////////////////////////////////////////// void CSystem::SetAffinity() { // the following code is only for Windows #ifdef WIN32 // set the process affinity ICVar* pcvAffinityMask = GetIConsole()->GetCVar("sys_affinity"); if (!pcvAffinityMask) { pcvAffinityMask = REGISTER_INT("sys_affinity", 0, VF_NULL, ""); } if (pcvAffinityMask) { unsigned nAffinity = pcvAffinityMask->GetIVal(); if (nAffinity) { typedef BOOL (WINAPI * FnSetProcessAffinityMask)(IN HANDLE hProcess, IN DWORD_PTR dwProcessAffinityMask); HMODULE hKernel = CryLoadLibrary ("kernel32.dll"); if (hKernel) { FnSetProcessAffinityMask SetProcessAffinityMask = (FnSetProcessAffinityMask)GetProcAddress(hKernel, "SetProcessAffinityMask"); if (SetProcessAffinityMask && !SetProcessAffinityMask(GetCurrentProcess(), nAffinity)) { GetILog()->LogError("Error: Cannot set affinity mask %d, error code %d", nAffinity, GetLastError()); } FreeLibrary (hKernel); } } } #endif } //! dumps the memory usage statistics to the log ////////////////////////////////////////////////////////////////////////// void CSystem::DumpMemoryUsageStatistics(bool bUseKB) { // CResourceCollector ResourceCollector; // TickMemStats(nMSP_ForDump,&ResourceCollector); TickMemStats(nMSP_ForDump); CrySizerStatsRenderer StatsRenderer (this, m_pMemStats, 10, 0); StatsRenderer.dump(bUseKB); // since we've recalculated this mem stats for dumping, we'll want to calculate it anew the next time it's rendered SAFE_DELETE(m_pMemStats); } // collects the whole memory statistics into the given sizer object ////////////////////////////////////////////////////////////////////////// #if defined(WIN32) #pragma pack(push,1) struct PEHeader_DLL { DWORD signature; IMAGE_FILE_HEADER _head; IMAGE_OPTIONAL_HEADER opt_head; IMAGE_SECTION_HEADER* section_header; // actual number in NumberOfSections }; #pragma pack(pop) #endif const SmallModuleInfo* FindModuleInfo(std::vector& vec, const char* name) { for (size_t i = 0; i < vec.size(); ++i) { if (!vec[i].name.compareNoCase(name)) { return &vec[i]; } } return 0; } ////////////////////////////////////////////////////////////////////////// void CSystem::CollectMemInfo(SCryEngineStatsGlobalMemInfo& m_stats) { m_stats.totalUsedInModules = 0; m_stats.totalCodeAndStatic = 0; m_stats.countedMemoryModules = 0; m_stats.totalAllocatedInModules = 0; m_stats.totalNumAllocsInModules = 0; AZStd::vector szModules = GetModuleNames(); const int numModules = szModules.size(); ////////////////////////////////////////////////////////////////////////// // Hardcoded value for the OS memory allocation. ////////////////////////////////////////////////////////////////////////// for (int i = 0; i < numModules; i++) { const char* szModule = szModules[i].c_str(); SCryEngineStatsModuleInfo moduleInfo; ZeroStruct(moduleInfo.memInfo); moduleInfo.moduleStaticSize = moduleInfo.SizeOfCode = moduleInfo.SizeOfInitializedData = moduleInfo.SizeOfUninitializedData = moduleInfo.usedInModule = 0; moduleInfo.name = szModule; if (!QueryModuleMemoryInfo(moduleInfo, i)) { continue; } m_stats.totalNumAllocsInModules += moduleInfo.memInfo.num_allocations; m_stats.totalAllocatedInModules += moduleInfo.memInfo.allocated; m_stats.totalUsedInModules += moduleInfo.usedInModule; m_stats.countedMemoryModules++; m_stats.totalCodeAndStatic += moduleInfo.moduleStaticSize; m_stats.modules.push_back(moduleInfo); } } void CSystem::CollectMemStats (ICrySizer* pSizer, MemStatsPurposeEnum nPurpose, std::vector* pStats) { std::vector stats; if (pStats) { pStats->assign(stats.begin(), stats.end()); } if (nMSP_ForCrashLog == nPurpose || nMSP_ForBudget == nPurpose) { return; } { SIZER_COMPONENT_NAME(pSizer, "CrySystem"); { pSizer->AddObject(this, sizeof(*this)); { //SIZER_COMPONENT_NAME (pSizer, "$Allocations waste"); //const SmallModuleInfo* info = FindModuleInfo(stats, "CrySystem.dll"); //if (info) //pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested ); } { SIZER_COMPONENT_NAME(pSizer, "VFS"); if (m_pStreamEngine) { SIZER_COMPONENT_NAME(pSizer, "Stream Engine"); m_pStreamEngine->GetMemoryStatistics(pSizer); } if (m_env.pCryPak) { SIZER_COMPONENT_NAME(pSizer, "CryPak"); ((CCryPak*)m_env.pCryPak)->GetMemoryStatistics(pSizer); g_pPakHeap->GetMemoryUsage(pSizer); } } { SIZER_COMPONENT_NAME(pSizer, "Localization Data"); m_pLocalizationManager->GetMemoryUsage(pSizer); } { SIZER_COMPONENT_NAME(pSizer, "XML"); m_pXMLUtils->GetMemoryUsage(pSizer); } if (m_env.pConsole) { SIZER_COMPONENT_NAME (pSizer, "Console"); m_env.pConsole->GetMemoryUsage (pSizer); } if (m_env.pLog) { SIZER_COMPONENT_NAME (pSizer, "Log"); m_env.pLog->GetMemoryUsage(pSizer); } } if (m_pPlatformOS.get()) { SIZER_COMPONENT_NAME(pSizer, "PlatformOS"); m_pPlatformOS->GetMemoryUsage(pSizer); } } if (m_env.pCharacterManager) { SIZER_COMPONENT_NAME(pSizer, "CryAnimation"); { { SIZER_COMPONENT_NAME (pSizer, "$Allocations waste"); const SmallModuleInfo* info = FindModuleInfo(stats, "CryAnimation.dll"); if (info) { pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested); } } m_env.pCharacterManager->GetMemoryUsage(pSizer); } } #if ENABLE_CRY_PHYSICS if (m_env.pPhysicalWorld) { SIZER_COMPONENT_NAME(pSizer, "CryPhysics"); { { SIZER_COMPONENT_NAME (pSizer, "$Allocations waste"); const SmallModuleInfo* info = FindModuleInfo(stats, "CryPhysics.dll"); if (info) { pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested); } } m_env.pPhysicalWorld->GetMemoryStatistics (pSizer); } } #endif // ENABLE_CRY_PHYSICS if (m_env.p3DEngine) { SIZER_COMPONENT_NAME(pSizer, "Cry3DEngine"); { m_env.p3DEngine->GetMemoryUsage (pSizer); { SIZER_COMPONENT_NAME (pSizer, "$Allocations waste"); const SmallModuleInfo* info = FindModuleInfo(stats, "Cry3DEngine.dll"); if (info) { pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested); } } } } if (m_env.pRenderer) { SIZER_COMPONENT_NAME(pSizer, "CryRenderer"); { { SIZER_COMPONENT_NAME (pSizer, "$Allocations waste D3D9"); const SmallModuleInfo* info = FindModuleInfo(stats, "CryRenderD3D9.dll"); if (info) { pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested); } } { SIZER_COMPONENT_NAME (pSizer, "$Allocations waste D3D10"); const SmallModuleInfo* info = FindModuleInfo(stats, "CryRenderD3D10.dll"); if (info) { pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested); } } m_env.pRenderer->GetMemoryUsage(pSizer); } } if (m_env.pCryFont) { SIZER_COMPONENT_NAME(pSizer, "CryFont"); { { SIZER_COMPONENT_NAME (pSizer, "$Allocations waste"); const SmallModuleInfo* info = FindModuleInfo(stats, "CryFont.dll"); if (info) { pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested); } } m_env.pCryFont->GetMemoryUsage(pSizer); // m_pIFont and m_pIFontUi are both counted in pCryFont sizing if they exist. // no need to manually add them here. } } if (m_env.pScriptSystem) { SIZER_COMPONENT_NAME(pSizer, "CryScriptSystem"); { { SIZER_COMPONENT_NAME (pSizer, "$Allocations waste"); const SmallModuleInfo* info = FindModuleInfo(stats, "CryScriptSystem.dll"); if (info) { pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested); } } m_env.pScriptSystem->GetMemoryStatistics(pSizer); } } if (m_env.pAISystem) { SIZER_COMPONENT_NAME(pSizer, "CryAISystem"); { { SIZER_COMPONENT_NAME (pSizer, "$Allocations waste"); const SmallModuleInfo* info = FindModuleInfo(stats, "CryAISystem.dll"); if (info) { pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested); } } m_env.pAISystem->GetMemoryStatistics (pSizer); } } if (m_env.pGame) { { SIZER_COMPONENT_NAME(pSizer, "Game"); { SIZER_COMPONENT_NAME (pSizer, "$Allocations waste"); const char* szGameDllName = gEnv->pConsole->GetCVar("sys_dll_game")->GetString(); const SmallModuleInfo* info = FindModuleInfo(stats, szGameDllName); if (info) { pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested); } } m_env.pGame->GetMemoryStatistics (pSizer); m_env.pGame->GetIGameFramework()->GetMemoryUsage(pSizer); } } if (m_env.pNetwork) { SIZER_COMPONENT_NAME(pSizer, "Network"); { SIZER_COMPONENT_NAME (pSizer, "$Allocations waste"); const SmallModuleInfo* info = FindModuleInfo(stats, "CryNetwork.dll"); if (info) { pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested); } } m_env.pNetwork->GetMemoryStatistics(pSizer); } if (m_env.pEntitySystem) { SIZER_COMPONENT_NAME(pSizer, "CryEntitySystem"); { SIZER_COMPONENT_NAME (pSizer, "$Allocations waste"); const SmallModuleInfo* info = FindModuleInfo(stats, "CryEntitySystem.dll"); if (info) { pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested); } } m_env.pEntitySystem->GetMemoryStatistics(pSizer); } { SIZER_COMPONENT_NAME(pSizer, "UserData"); if (m_pUserCallback) { m_pUserCallback->GetMemoryUsage(pSizer); } } #ifdef WIN32 { SIZER_COMPONENT_NAME(pSizer, "Code"); GetExeSizes (pSizer, nPurpose); } #endif pSizer->End(); } ////////////////////////////////////////////////////////////////////////// const char* CSystem::GetUserName() { #if defined(WIN32) || defined(WIN64) static const int iNameBufferSize = 1024; static char szNameBuffer[iNameBufferSize]; memset(szNameBuffer, 0, iNameBufferSize); DWORD dwSize = iNameBufferSize; wchar_t nameW[iNameBufferSize]; ::GetUserNameW(nameW, &dwSize); cry_strcpy(szNameBuffer, CryStringUtils::WStrToUTF8(nameW)); return szNameBuffer; #else #if defined(LINUX) static uid_t uid = geteuid (); static struct passwd* pw = getpwuid (uid); if (pw) { return (pw->pw_name); } else { return NULL; } #elif defined(APPLE) static const int iNameBufferSize = 1024; static char szNameBuffer[iNameBufferSize]; if(SystemUtilsApple::GetUserName(szNameBuffer, iNameBufferSize)) { return szNameBuffer; } else { return ""; } #else return ""; #endif #endif } ////////////////////////////////////////////////////////////////////////// int CSystem::GetApplicationInstance() { #ifdef WIN32 // tools that declare themselves as in "tool mode" may not access @user@ and may also not lock it if (gEnv->IsInToolMode()) { return 0; } // this code below essentially "locks" an instance of the USER folder to a specific running application if (m_iApplicationInstance == -1) { string suffix; for (int instance = 0;; ++instance) { suffix.Format("(%d)", instance); HANDLE instanceMutex = CreateMutex(NULL, TRUE, "LumberyardApplication" + suffix); // search for duplicates if (GetLastError() != ERROR_ALREADY_EXISTS) { m_iApplicationInstance = instance; break; } } } return m_iApplicationInstance; #else return 0; #endif } int CSystem::GetApplicationLogInstance(const char* logFilePath) { #if AZ_TRAIT_OS_USE_WINDOWS_MUTEX string suffix; int instance = 0; for (;; ++instance) { suffix.Format("(%d)", instance); HANDLE instanceMutex = CreateMutex(NULL, TRUE, logFilePath + suffix); if (GetLastError() != ERROR_ALREADY_EXISTS) { break; } } return instance; #else return 0; #endif } // refreshes the m_pMemStats if necessary; creates it if it's not created ////////////////////////////////////////////////////////////////////////// void CSystem::TickMemStats(MemStatsPurposeEnum nPurpose, IResourceCollector* pResourceCollector) { // gather the statistics, if required // if there's no object, or if it's time to recalculate, or if it's for dump, then recalculate it if (!m_pMemStats || (m_env.pRenderer->GetFrameID(false) % m_cvMemStats->GetIVal()) == 0 || nPurpose == nMSP_ForDump) { if (!m_pMemStats) { if (m_cvMemStats->GetIVal() < 4 && m_cvMemStats->GetIVal()) { GetILog()->LogToConsole("memstats is too small (%d). Performance impact can be significant. Please set to a greater value.", m_cvMemStats->GetIVal()); } m_pMemStats = new CrySizerStats(); } if (!m_pSizer) { m_pSizer = new CrySizerImpl(); } m_pSizer->SetResourceCollector(pResourceCollector); m_pMemStats->startTimer(0, GetITimer()); CollectMemStats (m_pSizer, nPurpose); m_pMemStats->stopTimer(0, GetITimer()); m_pMemStats->startTimer(1, GetITimer()); CrySizerStatsBuilder builder (m_pSizer); builder.build (m_pMemStats); m_pMemStats->stopTimer(1, GetITimer()); m_pMemStats->startTimer(2, GetITimer()); m_pSizer->clear(); m_pMemStats->stopTimer(2, GetITimer()); } else { m_pMemStats->incAgeFrames(); } } //#define __HASXP // these 2 functions are duplicated in System.cpp in editor ////////////////////////////////////////////////////////////////////////// #if !defined(LINUX) extern int CryStats(char* buf); #endif int CSystem::DumpMMStats(bool log) { #if defined(LINUX) return 0; #else if (log) { char buf[1024]; int n = CryStats(buf); GetILog()->Log(buf); return n; } else { return CryStats(NULL); }; #endif }; ////////////////////////////////////////////////////////////////////////// struct CryDbgModule { HANDLE heap; WIN_HMODULE handle; string name; DWORD dwSize; }; ////////////////////////////////////////////////////////////////////////// void CSystem::DebugStats(bool checkpoint, bool leaks) { #ifdef WIN32 std::vector dbgmodules; ////////////////////////////////////////////////////////////////////////// // Use windows Performance Monitoring API to enumerate all modules of current process. ////////////////////////////////////////////////////////////////////////// HANDLE hSnapshot; hSnapshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0); if (hSnapshot != INVALID_HANDLE_VALUE) { MODULEENTRY32 me; memset (&me, 0, sizeof(me)); me.dwSize = sizeof(me); if (Module32First (hSnapshot, &me)) { // the sizes of each module group do { CryDbgModule module; module.handle = me.hModule; module.name = me.szModule; module.dwSize = me.modBaseSize; dbgmodules.push_back(module); } while (Module32Next(hSnapshot, &me)); } CloseHandle (hSnapshot); } ////////////////////////////////////////////////////////////////////////// ILog* log = GetILog(); int totalal = 0, totalbl = 0, nolib = 0; #ifdef _DEBUG int extrastats[10]; #endif int totalUsedInModules = 0; int countedMemoryModules = 0; for (int i = 0; i < (int)(dbgmodules.size()); i++) { if (!dbgmodules[i].handle) { CryLogAlways("WARNING: CSystem::DebugStats: NULL handle for %s", dbgmodules[i].name.c_str()); nolib++; continue; } ; typedef int (* PFN_MODULEMEMORY)(); PFN_MODULEMEMORY fpCryModuleGetAllocatedMemory = (PFN_MODULEMEMORY)::GetProcAddress((HMODULE)dbgmodules[i].handle, "CryModuleGetAllocatedMemory"); if (fpCryModuleGetAllocatedMemory) { int allocatedMemory = fpCryModuleGetAllocatedMemory(); totalUsedInModules += allocatedMemory; countedMemoryModules++; CryLogAlways("%8d K used in Module %s: ", allocatedMemory / 1024, dbgmodules[i].name.c_str()); } #ifdef _DEBUG typedef void (* PFNUSAGESUMMARY)(ILog* log, const char*, int*); typedef void (* PFNCHECKPOINT)(); PFNUSAGESUMMARY fpu = (PFNUSAGESUMMARY)::GetProcAddress((HMODULE)dbgmodules[i].handle, "UsageSummary"); PFNCHECKPOINT fpc = (PFNCHECKPOINT)::GetProcAddress((HMODULE)dbgmodules[i].handle, "CheckPoint"); if (fpu && fpc) { if (checkpoint) { fpc(); } else { extrastats[2] = (int)leaks; fpu(log, dbgmodules[i].name.c_str(), extrastats); totalal += extrastats[0]; totalbl += extrastats[1]; }; } else { CryLogAlways("WARNING: CSystem::DebugStats: could not retrieve function from DLL %s", dbgmodules[i].name.c_str()); nolib++; }; #endif typedef HANDLE(* PFNGETDLLHEAP)(); PFNGETDLLHEAP fpg = (PFNGETDLLHEAP)::GetProcAddress((HMODULE)dbgmodules[i].handle, "GetDLLHeap"); if (fpg) { dbgmodules[i].heap = fpg(); } ; } ; CryLogAlways("-------------------------------------------------------"); CryLogAlways("%8d K Total Memory Allocated in %d Modules", totalUsedInModules / 1024, countedMemoryModules); #ifdef _DEBUG CryLogAlways("$8GRAND TOTAL: %d k, %d blocks (%d dlls not included)", totalal / 1024, totalbl, nolib); CryLogAlways("estimated debugalloc overhead: between %d k and %d k", totalbl * 36 / 1024, totalbl * 72 / 1024); #endif ////////////////////////////////////////////////////////////////////////// // Get HeapQueryInformation pointer if on windows XP. ////////////////////////////////////////////////////////////////////////// typedef BOOL (WINAPI * FUNC_HeapQueryInformation)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T); FUNC_HeapQueryInformation pFnHeapQueryInformation = NULL; HMODULE hKernelInstance = CryLoadLibrary("Kernel32.dll"); if (hKernelInstance) { pFnHeapQueryInformation = (FUNC_HeapQueryInformation)(::GetProcAddress(hKernelInstance, "HeapQueryInformation")); } ////////////////////////////////////////////////////////////////////////// const int MAXHANDLES = 100; HANDLE handles[MAXHANDLES]; int realnumh = GetProcessHeaps(MAXHANDLES, handles); char hinfo[1024]; PROCESS_HEAP_ENTRY phe; CryLogAlways("$6--------------------- dump of windows heaps ---------------------"); int nTotalC = 0, nTotalCP = 0, nTotalUC = 0, nTotalUCP = 0, totalo = 0; for (int i = 0; i < realnumh; i++) { HANDLE hHeap = handles[i]; HeapCompact(hHeap, 0); hinfo[0] = 0; if (pFnHeapQueryInformation) { pFnHeapQueryInformation(hHeap, HeapCompatibilityInformation, hinfo, 1024, NULL); } else { for (int m = 0; m < (int)(dbgmodules.size()); m++) { if (dbgmodules[m].heap == handles[i]) { azstrcpy(hinfo, AZ_ARRAY_SIZE(hinfo), dbgmodules[m].name.c_str()); } } } phe.lpData = NULL; int nCommitted = 0, nUncommitted = 0, nOverhead = 0; int nCommittedPieces = 0, nUncommittedPieces = 0; int nPrevRegionIndex = -1; while (HeapWalk(hHeap, &phe)) { if (phe.wFlags & PROCESS_HEAP_REGION) { assert (++nPrevRegionIndex == phe.iRegionIndex); nCommitted += phe.Region.dwCommittedSize; nUncommitted += phe.Region.dwUnCommittedSize; assert (phe.cbData == 0 || (phe.wFlags & PROCESS_HEAP_ENTRY_BUSY)); } else if (phe.wFlags & PROCESS_HEAP_UNCOMMITTED_RANGE) { nUncommittedPieces += phe.cbData; } else { //if (phe.wFlags & PROCESS_HEAP_ENTRY_BUSY) nCommittedPieces += phe.cbData; } { /* MEMORY_BASIC_INFORMATION mbi; if (VirtualQuery(phe.lpData, &mbi,sizeof(mbi)) == sizeof(mbi)) { if (mbi.State == MEM_COMMIT) nCommittedPieces += phe.cbData;//mbi.RegionSize; //else // nUncommitted += mbi.RegionSize; } else nCommittedPieces += phe.cbData; */ } nOverhead += phe.cbOverhead; } ; int nCommittedMin = min(nCommitted, nCommittedPieces); int nCommittedMax = max(nCommitted, nCommittedPieces); CryLogAlways("* heap %8x: %6d (or ~%6d) K in use, %6d..%6d K uncommitted, %6d K overhead (%s)\n", handles[i], nCommittedPieces / 1024, nCommitted / 1024, nUncommittedPieces / 1024, nUncommitted / 1024, nOverhead / 1024, hinfo); nTotalC += nCommitted; nTotalCP += nCommittedPieces; nTotalUC += nUncommitted; nTotalUCP += nUncommittedPieces; totalo += nOverhead; } ; CryLogAlways("$6----------------- total in heaps: %d megs committed (win stats shows ~%d) (%d..%d uncommitted, %d k overhead) ---------------------", nTotalCP / 1024 / 1024, nTotalC / 1024 / 1024, nTotalUCP / 1024 / 1024, nTotalUC / 1024 / 1024, totalo / 1024); #endif //WIN32 }; #ifdef WIN32 struct DumpHeap32Stats { DumpHeap32Stats() : dwFree(0) , dwMoveable(0) , dwFixed(0) , dwUnknown(0) { } void operator += (const DumpHeap32Stats& right) { dwFree += right.dwFree; dwMoveable += right.dwMoveable; dwFixed += right.dwFixed; dwUnknown += right.dwUnknown; } DWORD dwFree; DWORD dwMoveable; DWORD dwFixed; DWORD dwUnknown; }; static void DumpHeap32 (const HEAPLIST32& hl, DumpHeap32Stats& stats) { HEAPENTRY32 he; memset (&he, 0, sizeof(he)); he.dwSize = sizeof(he); if (Heap32First (&he, hl.th32ProcessID, hl.th32HeapID)) { DumpHeap32Stats heap; do { if (he.dwFlags & LF32_FREE) { heap.dwFree += he.dwBlockSize; } else if (he.dwFlags & LF32_MOVEABLE) { heap.dwMoveable += he.dwBlockSize; } else if (he.dwFlags & LF32_FIXED) { heap.dwFixed += he.dwBlockSize; } else { heap.dwUnknown += he.dwBlockSize; } } while (Heap32Next (&he)); CryLogAlways ("%08X %6d %6d %6d (%d)", hl.th32HeapID, heap.dwFixed / 0x400, heap.dwFree / 0x400, heap.dwMoveable / 0x400, heap.dwUnknown / 0x400); stats += heap; } else { CryLogAlways ("%08X empty or invalid"); } } ////////////////////////////////////////////////////////////////////////// class CStringOrder { public: bool operator () (const char* szLeft, const char* szRight) const {return azstricmp(szLeft, szRight) < 0; } }; typedef std::map StringToSizeMap; void AddSize (StringToSizeMap& mapSS, const char* szString, unsigned nSize) { StringToSizeMap::iterator it = mapSS.find (szString); if (it == mapSS.end()) { mapSS.insert (StringToSizeMap::value_type(szString, nSize)); } else { it->second += nSize; } } ////////////////////////////////////////////////////////////////////////// const char* GetModuleGroup (const char* szString) { for (unsigned i = 0; i < sizeof(g_szModuleGroups) / sizeof(g_szModuleGroups[0]); ++i) { if (azstricmp(szString, g_szModuleGroups[i][0]) == 0) { return g_szModuleGroups[i][1]; } } return "Other"; } ////////////////////////////////////////////////////////////////////////// void CSystem::GetExeSizes (ICrySizer* pSizer, MemStatsPurposeEnum nPurpose) { HANDLE hSnapshot; hSnapshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0); if (hSnapshot == INVALID_HANDLE_VALUE) { CryLogAlways ("Cannot get the module snapshot, error code %d", GetLastError()); return; } DWORD dwProcessID = GetCurrentProcessId(); MODULEENTRY32 me; memset (&me, 0, sizeof(me)); me.dwSize = sizeof(me); if (Module32First (hSnapshot, &me)) { // the sizes of each module group StringToSizeMap mapGroupSize; DWORD dwTotalModuleSize = 0; do { dwProcessID = me.th32ProcessID; const char* szGroup = GetModuleGroup (me.szModule); SIZER_COMPONENT_NAME(pSizer, szGroup); if (nPurpose == nMSP_ForDump) { SIZER_COMPONENT_NAME(pSizer, me.szModule); pSizer->AddObject(me.modBaseAddr, me.modBaseSize); } else { pSizer->AddObject(me.modBaseAddr, me.modBaseSize); } } while (Module32Next(hSnapshot, &me)); } else { CryLogAlways ("No modules to dump"); } CloseHandle (hSnapshot); } #endif ////////////////////////////////////////////////////////////////////////// void CSystem::DumpWinHeaps() { #ifdef WIN32 // // Retrieve modules and log them; remember the process id HANDLE hSnapshot; hSnapshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0); if (hSnapshot == INVALID_HANDLE_VALUE) { CryLogAlways ("Cannot get the module snapshot, error code %d", GetLastError()); return; } DWORD dwProcessID = GetCurrentProcessId(); MODULEENTRY32 me; memset (&me, 0, sizeof(me)); me.dwSize = sizeof(me); if (Module32First (hSnapshot, &me)) { // the sizes of each module group StringToSizeMap mapGroupSize; DWORD dwTotalModuleSize = 0; CryLogAlways ("base size module"); do { dwProcessID = me.th32ProcessID; const char* szGroup = GetModuleGroup (me.szModule); CryLogAlways ("%08X %8X %25s - %s", me.modBaseAddr, me.modBaseSize, me.szModule, azstricmp(szGroup, "Other") ? szGroup : ""); dwTotalModuleSize += me.modBaseSize; AddSize (mapGroupSize, szGroup, me.modBaseSize); } while (Module32Next(hSnapshot, &me)); CryLogAlways ("------------------------------------"); for (StringToSizeMap::iterator it = mapGroupSize.begin(); it != mapGroupSize.end(); ++it) { CryLogAlways (" %6.3f Mbytes - %s", double(it->second) / 0x100000, it->first); } CryLogAlways ("------------------------------------"); CryLogAlways (" %6.3f Mbytes - TOTAL", double(dwTotalModuleSize) / 0x100000); CryLogAlways ("------------------------------------"); } else { CryLogAlways ("No modules to dump"); } CloseHandle (hSnapshot); // // Retrieve the heaps and dump each of them with a special function hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, 0); if (hSnapshot == INVALID_HANDLE_VALUE) { CryLogAlways ("Cannot get the heap LIST snapshot, error code %d", GetLastError()); return; } HEAPLIST32 hl; memset (&hl, 0, sizeof(hl)); hl.dwSize = sizeof(hl); CryLogAlways ("__Heap__ fixed free move (unknown)"); if (Heap32ListFirst (hSnapshot, &hl)) { DumpHeap32Stats stats; do { DumpHeap32 (hl, stats); } while (Heap32ListNext (hSnapshot, &hl)); CryLogAlways ("-------------------------------------------------"); CryLogAlways ("$6 %6.3f %6.3f %6.3f (%.3f) Mbytes", double(stats.dwFixed) / 0x100000, double(stats.dwFree) / 0x100000, double(stats.dwMoveable) / 0x100000, double(stats.dwUnknown) / 0x100000); CryLogAlways ("-------------------------------------------------"); } else { CryLogAlways ("No heaps to dump"); } CloseHandle(hSnapshot); #endif } // Make system error message string ////////////////////////////////////////////////////////////////////////// //! \return pointer to the null terminated error string or 0 static const char* GetLastSystemErrorMessage() { #ifdef WIN32 DWORD dwError = GetLastError(); static char szBuffer[512]; // function will return pointer to this buffer if (dwError) { LPVOID lpMsgBuf = 0; if (FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &lpMsgBuf, 0, NULL)) { cry_strcpy(szBuffer, (char*)lpMsgBuf); LocalFree(lpMsgBuf); } else { return 0; } return szBuffer; } #else return 0; #endif //WIN32 return 0; } ////////////////////////////////////////////////////////////////////////// void CSystem::FatalError(const char* format, ...) { // Guard against reentrancy - out of memory fatal errors can become reentrant since logging can try to alloc. static bool currentlyReportingError = false; if (currentlyReportingError == true) { return; } currentlyReportingError = true; // format message va_list ArgList; char szBuffer[MAX_WARNING_LENGTH]; const char* sPrefix = ""; azstrcpy(szBuffer, MAX_WARNING_LENGTH, sPrefix); va_start(ArgList, format); azvsnprintf(szBuffer + strlen(sPrefix), MAX_WARNING_LENGTH - strlen(sPrefix), format, ArgList); va_end(ArgList); // get system error message before any attempt to write into log const char* szSysErrorMessage = GetLastSystemErrorMessage(); CryLogAlways("============================================================================="); CryLogAlways("*ERROR"); CryLogAlways("============================================================================="); // write both messages into log CryLogAlways("%s", szBuffer); if (szSysErrorMessage) { CryLogAlways(" Last System Error: %s", szSysErrorMessage); } if (const char* pLoadingProfilerCallstack = GetLoadingProfilerCallstack()) { if (pLoadingProfilerCallstack[0]) { CryLogAlways(" LoadingProfilerCallstack: %s", pLoadingProfilerCallstack); } } if (GetUserCallback()) { GetUserCallback()->OnError(szBuffer); } assert(szBuffer[0] >= ' '); // strcpy(szBuffer,szBuffer+1); // remove verbosity tag since it is not supported by ::MessageBox LogSystemInfo(); CollectMemStats(0, nMSP_ForCrashLog); OutputDebugString(szBuffer); #ifdef WIN32 OnFatalError(szBuffer); if (!g_cvars.sys_no_crash_dialog) { ICVar* pFullscreen = (gEnv && gEnv->pConsole) ? gEnv->pConsole->GetCVar("r_Fullscreen") : 0; if (pFullscreen && pFullscreen->GetIVal() != 0 && gEnv->pRenderer && gEnv->pRenderer->GetHWND()) { ::ShowWindow((HWND)gEnv->pRenderer->GetHWND(), SW_MINIMIZE); } ::MessageBox(NULL, szBuffer, "Lumberyard Error", MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); } // Dump callstack. #endif #if defined (WIN32) //Triggers a fatal error, so the DebugCallstack can create the error.log and terminate the application IDebugCallStack::instance()->FatalError(szBuffer); #elif defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION SYSTEMWIN32_CPP_SECTION_1 #if defined(AZ_PLATFORM_XENIA) #include "Xenia/SystemWin32_cpp_xenia.inl" #elif defined(AZ_PLATFORM_PROVO) #include "Provo/SystemWin32_cpp_provo.inl" #elif defined(AZ_PLATFORM_SALEM) #include "Salem/SystemWin32_cpp_salem.inl" #endif #endif CryDebugBreak(); // app can not continue #ifdef _DEBUG #if defined(WIN32) && !defined(WIN64) DEBUG_BREAK; #endif #else #if defined(WIN32) || defined(WIN64) _flushall(); // on windows, _exit does all sorts of things which can cause cleanup to fail during a crash, we need to terminate instead. TerminateProcess(GetCurrentProcess(), 1); #endif #if defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION SYSTEMWIN32_CPP_SECTION_2 #if defined(AZ_PLATFORM_XENIA) #include "Xenia/SystemWin32_cpp_xenia.inl" #elif defined(AZ_PLATFORM_PROVO) #include "Provo/SystemWin32_cpp_provo.inl" #elif defined(AZ_PLATFORM_SALEM) #include "Salem/SystemWin32_cpp_salem.inl" #endif #endif #if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED) #undef AZ_RESTRICTED_SECTION_IMPLEMENTED #else _exit(1); #endif #endif } void CSystem::ReportBug(const char* format, ...) { #if defined (WIN32) va_list ArgList; char szBuffer[MAX_WARNING_LENGTH]; const char* sPrefix = ""; azstrcpy(szBuffer, MAX_WARNING_LENGTH, sPrefix); va_start(ArgList, format); azvsnprintf(szBuffer + strlen(sPrefix), MAX_WARNING_LENGTH - strlen(sPrefix), format, ArgList); va_end(ArgList); IDebugCallStack::instance()->ReportBug(szBuffer); #endif } ////////////////////////////////////////////////////////////////////////// void CSystem::debug_GetCallStack(const char** pFunctions, int& nCount) { #if defined(WIN32) using namespace AZ::Debug; int nMaxCount = nCount; StackFrame* frames = (StackFrame*)AZ_ALLOCA(sizeof(StackFrame)*nMaxCount); unsigned int numFrames = StackRecorder::Record(frames, nMaxCount, 1); SymbolStorage::StackLine* textLines = (SymbolStorage::StackLine*)AZ_ALLOCA(sizeof(SymbolStorage::StackLine)*nMaxCount); SymbolStorage::DecodeFrames(frames, numFrames, textLines); for (int i = 0; i < numFrames; i++) { pFunctions[i] = textLines[i]; } nCount = numFrames; #define AZ_RESTRICTED_SECTION_IMPLEMENTED #elif defined(AZ_RESTRICTED_PLATFORM) #define AZ_RESTRICTED_SECTION SYSTEMWIN32_CPP_SECTION_3 #if defined(AZ_PLATFORM_XENIA) #include "Xenia/SystemWin32_cpp_xenia.inl" #elif defined(AZ_PLATFORM_PROVO) #include "Provo/SystemWin32_cpp_provo.inl" #elif defined(AZ_PLATFORM_SALEM) #include "Salem/SystemWin32_cpp_salem.inl" #endif #endif #if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED) #undef AZ_RESTRICTED_SECTION_IMPLEMENTED #else nCount = 0; #endif } ////////////////////////////////////////////////////////////////////////// void CSystem::debug_LogCallStack(int nMaxFuncs, int nFlags) { if (nMaxFuncs > 32) { nMaxFuncs = 32; } // Print call stack for each find. const char* funcs[32]; int nCount = nMaxFuncs; int nCurFrame = 0; if (m_env.pRenderer) { nCurFrame = (int)m_env.pRenderer->GetFrameID(false); } CryLogAlways(" ----- CallStack (Frame: %d) -----", nCurFrame); GetISystem()->debug_GetCallStack(funcs, nCount); for (int i = 1; i < nCount; i++) // start from 1 to skip this function. { CryLogAlways(" %02d) %s", i, funcs[i]); } } ////////////////////////////////////////////////////////////////////////// // Support relaunching for windows media center edition. ////////////////////////////////////////////////////////////////////////// #if defined(WIN32) #if (_WIN32_WINNT < 0x0501) #define SM_MEDIACENTER 87 #endif bool CSystem::ReLaunchMediaCenter() { // Skip if not running on a Media Center if (GetSystemMetrics(SM_MEDIACENTER) == 0) { return false; } // Get the path to Media Center char szExpandedPath[AZ_MAX_PATH_LEN]; if (!ExpandEnvironmentStrings("%SystemRoot%\\ehome\\ehshell.exe", szExpandedPath, AZ_MAX_PATH_LEN)) { return false; } // Skip if ehshell.exe doesn't exist if (GetFileAttributes(szExpandedPath) == 0xFFFFFFFF) { return false; } // Launch ehshell.exe INT_PTR result = (INT_PTR)ShellExecute(NULL, TEXT("open"), szExpandedPath, NULL, NULL, SW_SHOWNORMAL); return (result > 32); } #else bool CSystem::ReLaunchMediaCenter() { return false; } #endif //defined(WIN32) ////////////////////////////////////////////////////////////////////////// #if defined(WIN32) void CSystem::LogSystemInfo() { ////////////////////////////////////////////////////////////////////// // Write the system informations to the log ////////////////////////////////////////////////////////////////////// char szBuffer[1024]; char szProfileBuffer[128]; char szLanguageBuffer[64]; //char szCPUModel[64]; char* pChar = 0; MEMORYSTATUSEX MemoryStatus; MemoryStatus.dwLength = sizeof(MemoryStatus); DEVMODE DisplayConfig; OSVERSIONINFO OSVerInfo; OSVerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); // log Windows type Win32SysInspect::GetOS(m_env.pi.winVer, m_env.pi.win64Bit, szBuffer, sizeof(szBuffer)); CryLogAlways(szBuffer); // log system language GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_SENGLANGUAGE, szLanguageBuffer, sizeof(szLanguageBuffer)); azsprintf(szBuffer, "System language: %s", szLanguageBuffer); CryLogAlways(szBuffer); // log Windows directory GetWindowsDirectory(szBuffer, sizeof(szBuffer)); string str = "Windows Directory: \""; str += szBuffer; str += "\""; CryLogAlways(str); ////////////////////////////////////////////////////////////////////// // Send system time & date ////////////////////////////////////////////////////////////////////// str = "Local time is "; _strtime_s(szBuffer); str += szBuffer; str += " "; _strdate_s(szBuffer); str += szBuffer; azsprintf(szBuffer, ", system running for %lu minutes", GetTickCount() / 60000); str += szBuffer; CryLogAlways(str); ////////////////////////////////////////////////////////////////////// // Send system memory status ////////////////////////////////////////////////////////////////////// GlobalMemoryStatusEx(&MemoryStatus); azsprintf(szBuffer, "%I64dMB physical memory installed, %I64dMB available, %I64dMB virtual memory installed, %ld percent of memory in use", MemoryStatus.ullTotalPhys / 1048576 + 1, MemoryStatus.ullAvailPhys / 1048576, MemoryStatus.ullTotalVirtual / 1048576, MemoryStatus.dwMemoryLoad); CryLogAlways(szBuffer); if (GetISystem()->GetIMemoryManager()) { IMemoryManager::SProcessMemInfo memCounters; GetISystem()->GetIMemoryManager()->GetProcessMemInfo(memCounters); uint64 PagefileUsage = memCounters.PagefileUsage; uint64 PeakPagefileUsage = memCounters.PeakPagefileUsage; uint64 WorkingSetSize = memCounters.WorkingSetSize; azsprintf(szBuffer, "PageFile usage: %I64dMB, Working Set: %I64dMB, Peak PageFile usage: %I64dMB,", (uint64)PagefileUsage / (1024 * 1024), (uint64)WorkingSetSize / (1024 * 1024), (uint64)PeakPagefileUsage / (1024 * 1024)); CryLogAlways(szBuffer); } ////////////////////////////////////////////////////////////////////// // Send display settings ////////////////////////////////////////////////////////////////////// EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &DisplayConfig); GetPrivateProfileString("boot.description", "display.drv", "(Unknown graphics card)", szProfileBuffer, sizeof(szProfileBuffer), "system.ini"); azsprintf(szBuffer, "Current display mode is %lux%lux%lu, %s", DisplayConfig.dmPelsWidth, DisplayConfig.dmPelsHeight, DisplayConfig.dmBitsPerPel, szProfileBuffer); CryLogAlways(szBuffer); ////////////////////////////////////////////////////////////////////// // Send input device configuration ////////////////////////////////////////////////////////////////////// str = ""; // Detect the keyboard type switch (GetKeyboardType(0)) { case 1: str = "IBM PC/XT (83-key)"; break; case 2: str = "ICO (102-key)"; break; case 3: str = "IBM PC/AT (84-key)"; break; case 4: str = "IBM enhanced (101/102-key)"; break; case 5: str = "Nokia 1050"; break; case 6: str = "Nokia 9140"; break; case 7: str = "Japanese"; break; default: str = "Unknown"; break; } // Any mouse attached ? if (!GetSystemMetrics(SM_MOUSEPRESENT)) { CryLogAlways(str + " keyboard and no mouse installed"); } else { azsprintf(szBuffer, " keyboard and %i+ button mouse installed", GetSystemMetrics(SM_CMOUSEBUTTONS)); CryLogAlways(str + szBuffer); } CryLogAlways("--------------------------------------------------------------------------------"); } #else void CSystem::LogSystemInfo() { } #endif #if (defined(WIN32) || defined(WIN64)) ////////////////////////////////////////////////////////////////////////// bool CSystem::GetWinGameFolder(char* szMyDocumentsPath, int maxPathSize) { bool bSucceeded = false; // check Vista and later OS first HMODULE shell32 = LoadLibraryA("Shell32.dll"); if (shell32) { typedef long (__stdcall * T_SHGetKnownFolderPath)(REFKNOWNFOLDERID rfid, unsigned long dwFlags, void* hToken, wchar_t** ppszPath); T_SHGetKnownFolderPath SHGetKnownFolderPath = (T_SHGetKnownFolderPath)GetProcAddress(shell32, "SHGetKnownFolderPath"); if (SHGetKnownFolderPath) { // We must be running Vista or newer wchar_t* wMyDocumentsPath; HRESULT hr = SHGetKnownFolderPath(FOLDERID_SavedGames, KF_FLAG_CREATE | KF_FLAG_DONT_UNEXPAND, NULL, &wMyDocumentsPath); bSucceeded = SUCCEEDED(hr); if (bSucceeded) { // Convert from UNICODE to UTF-8 cry_strcpy(szMyDocumentsPath, maxPathSize, CryStringUtils::WStrToUTF8(wMyDocumentsPath)); CoTaskMemFree(wMyDocumentsPath); } } FreeLibrary(shell32); } if (!bSucceeded) { // check pre-vista OS if not succeeded before wchar_t wMyDocumentsPath[AZ_MAX_PATH_LEN]; bSucceeded = SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, 0, wMyDocumentsPath)); if (bSucceeded) { cry_strcpy(szMyDocumentsPath, maxPathSize, CryStringUtils::WStrToUTF8(wMyDocumentsPath)); } } return bSucceeded; } #endif ////////////////////////////////////////////////////////////////////////// void CSystem::DetectGameFolderAccessRights() { // This code is trying to figure out if the current folder we are now running under have write access. // By default assume folder is not writable. // If folder is writable game.log is saved there, otherwise it is saved in user documents folder. #if defined(WIN32) DWORD DesiredAccess = FILE_GENERIC_WRITE; DWORD GrantedAccess = 0; DWORD dwRes = 0; PACL pDACL = NULL; PSECURITY_DESCRIPTOR pSD = NULL; HANDLE hClientToken = 0; PRIVILEGE_SET PrivilegeSet; DWORD PrivilegeSetLength = sizeof(PrivilegeSet); BOOL bAccessStatus = FALSE; // Get a pointer to the existing DACL. dwRes = GetNamedSecurityInfo(".", SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, NULL, NULL, &pDACL, NULL, &pSD); if (ERROR_SUCCESS != dwRes) { // assert(0); } if (!ImpersonateSelf(SecurityIdentification)) { return; } if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hClientToken) && hClientToken != 0) { return; } GENERIC_MAPPING GenMap; GenMap.GenericRead = FILE_GENERIC_READ; GenMap.GenericWrite = FILE_GENERIC_WRITE; GenMap.GenericExecute = FILE_GENERIC_EXECUTE; GenMap.GenericAll = FILE_ALL_ACCESS; MapGenericMask(&DesiredAccess, &GenMap); if (!AccessCheck(pSD, hClientToken, DesiredAccess, &GenMap, &PrivilegeSet, &PrivilegeSetLength, &GrantedAccess, &bAccessStatus)) { RevertToSelf(); CloseHandle(hClientToken); return; } CloseHandle(hClientToken); RevertToSelf(); if (bAccessStatus) { m_bGameFolderWritable = true; } #elif defined(MOBILE) char cwd[AZ_MAX_PATH_LEN]; if (getcwd(cwd, AZ_MAX_PATH_LEN) != NULL) { if (0 == access(cwd, W_OK)) { m_bGameFolderWritable = true; } } #endif //WIN32 } /////////////////////////////////`///////////////////////////////////////// void CSystem::EnableFloatExceptions(int type) { #ifndef _RELEASE #if defined(WIN32) #if defined(WIN32) && !defined(WIN64) // Optimization // Enable DAZ/FZ // Denormals Are Zeros // Flush-to-Zero _controlfp(_DN_FLUSH, _MCW_DN); #endif //#if defined(WIN32) && !defined(WIN64) #pragma warning( push ) #pragma warning(disable: 4996) _controlfp(_DN_FLUSH, _MCW_DN); if (type == 0) { // mask all floating exceptions off. _controlfp(_EM_INEXACT | _EM_UNDERFLOW | _EM_OVERFLOW | _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE, _MCW_EM); } else { // Clear pending exceptions _fpreset(); if (type == 1) { // enable just the most important fp-exceptions. _controlfp(_EM_INEXACT | _EM_UNDERFLOW | _EM_OVERFLOW, _MCW_EM); // Enable floating point exceptions. } if (type == 2) { // enable ALL floating point exceptions. _controlfp(_EM_INEXACT, _MCW_EM); } } #pragma warning( pop ) #endif //#if defined(WIN32) && !defined(WIN64) #ifdef WIN32 _mm_setcsr(_mm_getcsr() & ~0x280 | (type > 0 ? 0 : 0x280)); #endif #endif //_RELEASE }