SystemWin32.cpp 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554
  1. /*
  2. * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
  3. * its licensors.
  4. *
  5. * For complete copyright and license terms please see the LICENSE at the root of this
  6. * distribution (the "License"). All use of this software is governed by the License,
  7. * or, if provided, by the license below or the license accompanying this file. Do not
  8. * remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
  9. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. *
  11. */
  12. // Original file Copyright Crytek GMBH or its affiliates, used under license.
  13. #include "CrySystem_precompiled.h"
  14. #include "System.h"
  15. #include <time.h>
  16. #include <I3DEngine.h>
  17. #include <IRenderer.h>
  18. #include <IMovieSystem.h>
  19. #include <ILog.h>
  20. #include <CryLibrary.h>
  21. #include <StringUtils.h>
  22. #include <AzCore/Debug/StackTracer.h>
  23. #include <AzCore/IO/SystemFile.h> // for AZ_MAX_PATH_LEN
  24. #include <AzCore/std/allocator_stack.h>
  25. #if defined(AZ_RESTRICTED_PLATFORM)
  26. #undef AZ_RESTRICTED_SECTION
  27. #define SYSTEMWIN32_CPP_SECTION_1 1
  28. #define SYSTEMWIN32_CPP_SECTION_2 2
  29. #define SYSTEMWIN32_CPP_SECTION_3 3
  30. #endif
  31. #if defined(LINUX) || defined(APPLE)
  32. #include <unistd.h>
  33. #endif
  34. #ifdef WIN32
  35. #define WIN32_LEAN_AND_MEAN
  36. #include "windows.h"
  37. #include <float.h>
  38. #include <shellapi.h> // Needed for ShellExecute.
  39. #include <Psapi.h>
  40. #include <Aclapi.h>
  41. #include <shlobj.h>
  42. #endif
  43. #include "IDebugCallStack.h"
  44. #if defined(APPLE) || defined(LINUX)
  45. #include <pwd.h>
  46. #endif
  47. #include "XConsole.h"
  48. #include "CrySizerStats.h"
  49. #include "CrySizerImpl.h"
  50. #include "StreamEngine/StreamEngine.h"
  51. #include "LocalizedStringManager.h"
  52. #include "XML/XmlUtils.h"
  53. #include "AutoDetectSpec.h"
  54. #if defined(WIN32)
  55. __pragma(comment(lib, "wininet.lib"))
  56. __pragma(comment(lib, "Winmm.lib"))
  57. #endif
  58. #if defined(APPLE)
  59. #include "SystemUtilsApple.h"
  60. #endif
  61. static AZStd::vector<AZStd::string> GetModuleNames()
  62. {
  63. AZStd::vector<AZStd::string> moduleNames;
  64. if (moduleNames.empty())
  65. {
  66. # ifdef MODULE_EXTENSION
  67. # error MODULE_EXTENSION already defined!
  68. # endif
  69. # if defined(LINUX)
  70. # define MODULE_EXTENSION ".so"
  71. # elif defined(APPLE)
  72. # define MODULE_EXTENSION ".dylib"
  73. # else
  74. # define MODULE_EXTENSION ".dll"
  75. # endif
  76. moduleNames.push_back("Cry3DEngine" MODULE_EXTENSION);
  77. moduleNames.push_back("CryFont" MODULE_EXTENSION);
  78. moduleNames.push_back("CrySystem" MODULE_EXTENSION);
  79. #undef MODULE_EXTENSION
  80. # if defined(LINUX)
  81. moduleNames.push_back("CryRenderNULL.so");
  82. # elif defined(APPLE)
  83. moduleNames.push_back("CryRenderNULL.dylib");
  84. # else
  85. moduleNames.push_back("Editor.exe");
  86. moduleNames.push_back("CryRenderD3D9.dll");
  87. moduleNames.push_back("CryRenderD3D10.dll");
  88. moduleNames.push_back("CryRenderNULL.dll");
  89. //TODO: launcher?
  90. # endif
  91. }
  92. return moduleNames;
  93. }
  94. static bool QueryModuleMemoryInfo([[maybe_unused]] SCryEngineStatsModuleInfo& moduleInfo, [[maybe_unused]] int index)
  95. {
  96. return false;
  97. }
  98. // this is the list of modules that can be loaded into the game process
  99. // Each array element contains 2 strings: the name of the module (case-insensitive)
  100. // and the name of the group the module belongs to
  101. //////////////////////////////////////////////////////////////////////////
  102. const char g_szGroupCore[] = "CryEngine";
  103. const char* g_szModuleGroups[][2] = {
  104. {"Editor.exe", g_szGroupCore},
  105. {"CrySystem.dll", g_szGroupCore},
  106. {"CryFont.dll", g_szGroupCore},
  107. };
  108. //////////////////////////////////////////////////////////////////////////
  109. void CSystem::SetAffinity()
  110. {
  111. // the following code is only for Windows
  112. #ifdef WIN32
  113. // set the process affinity
  114. ICVar* pcvAffinityMask = GetIConsole()->GetCVar("sys_affinity");
  115. if (!pcvAffinityMask)
  116. {
  117. pcvAffinityMask = REGISTER_INT("sys_affinity", 0, VF_NULL, "");
  118. }
  119. if (pcvAffinityMask)
  120. {
  121. unsigned nAffinity = pcvAffinityMask->GetIVal();
  122. if (nAffinity)
  123. {
  124. typedef BOOL (WINAPI * FnSetProcessAffinityMask)(IN HANDLE hProcess, IN DWORD_PTR dwProcessAffinityMask);
  125. HMODULE hKernel = CryLoadLibrary ("kernel32.dll");
  126. if (hKernel)
  127. {
  128. FnSetProcessAffinityMask SetProcessAffinityMask = (FnSetProcessAffinityMask)GetProcAddress(hKernel, "SetProcessAffinityMask");
  129. if (SetProcessAffinityMask && !SetProcessAffinityMask(GetCurrentProcess(), nAffinity))
  130. {
  131. GetILog()->LogError("Error: Cannot set affinity mask %d, error code %d", nAffinity, GetLastError());
  132. }
  133. FreeLibrary (hKernel);
  134. }
  135. }
  136. }
  137. #endif
  138. }
  139. //! dumps the memory usage statistics to the log
  140. //////////////////////////////////////////////////////////////////////////
  141. void CSystem::DumpMemoryUsageStatistics(bool bUseKB)
  142. {
  143. // CResourceCollector ResourceCollector;
  144. // TickMemStats(nMSP_ForDump,&ResourceCollector);
  145. TickMemStats(nMSP_ForDump);
  146. CrySizerStatsRenderer StatsRenderer (this, m_pMemStats, 10, 0);
  147. StatsRenderer.dump(bUseKB);
  148. // since we've recalculated this mem stats for dumping, we'll want to calculate it anew the next time it's rendered
  149. SAFE_DELETE(m_pMemStats);
  150. }
  151. // collects the whole memory statistics into the given sizer object
  152. //////////////////////////////////////////////////////////////////////////
  153. #if defined(WIN32)
  154. #pragma pack(push,1)
  155. struct PEHeader_DLL
  156. {
  157. DWORD signature;
  158. IMAGE_FILE_HEADER _head;
  159. IMAGE_OPTIONAL_HEADER opt_head;
  160. IMAGE_SECTION_HEADER* section_header; // actual number in NumberOfSections
  161. };
  162. #pragma pack(pop)
  163. #endif
  164. const SmallModuleInfo* FindModuleInfo(std::vector<SmallModuleInfo>& vec, const char* name)
  165. {
  166. for (size_t i = 0; i < vec.size(); ++i)
  167. {
  168. if (!vec[i].name.compareNoCase(name))
  169. {
  170. return &vec[i];
  171. }
  172. }
  173. return 0;
  174. }
  175. //////////////////////////////////////////////////////////////////////////
  176. void CSystem::CollectMemInfo(SCryEngineStatsGlobalMemInfo& m_stats)
  177. {
  178. m_stats.totalUsedInModules = 0;
  179. m_stats.totalCodeAndStatic = 0;
  180. m_stats.countedMemoryModules = 0;
  181. m_stats.totalAllocatedInModules = 0;
  182. m_stats.totalNumAllocsInModules = 0;
  183. AZStd::vector<AZStd::string> szModules = GetModuleNames();
  184. const int numModules = szModules.size();
  185. //////////////////////////////////////////////////////////////////////////
  186. // Hardcoded value for the OS memory allocation.
  187. //////////////////////////////////////////////////////////////////////////
  188. for (int i = 0; i < numModules; i++)
  189. {
  190. const char* szModule = szModules[i].c_str();
  191. SCryEngineStatsModuleInfo moduleInfo;
  192. ZeroStruct(moduleInfo.memInfo);
  193. moduleInfo.moduleStaticSize = moduleInfo.SizeOfCode = moduleInfo.SizeOfInitializedData = moduleInfo.SizeOfUninitializedData = moduleInfo.usedInModule = 0;
  194. moduleInfo.name = szModule;
  195. if (!QueryModuleMemoryInfo(moduleInfo, i))
  196. {
  197. continue;
  198. }
  199. m_stats.totalNumAllocsInModules += moduleInfo.memInfo.num_allocations;
  200. m_stats.totalAllocatedInModules += moduleInfo.memInfo.allocated;
  201. m_stats.totalUsedInModules += moduleInfo.usedInModule;
  202. m_stats.countedMemoryModules++;
  203. m_stats.totalCodeAndStatic += moduleInfo.moduleStaticSize;
  204. m_stats.modules.push_back(moduleInfo);
  205. }
  206. }
  207. void CSystem::CollectMemStats (ICrySizer* pSizer, MemStatsPurposeEnum nPurpose, std::vector<SmallModuleInfo>* pStats)
  208. {
  209. std::vector<SmallModuleInfo> stats;
  210. if (pStats)
  211. {
  212. pStats->assign(stats.begin(), stats.end());
  213. }
  214. if (nMSP_ForCrashLog == nPurpose || nMSP_ForBudget == nPurpose)
  215. {
  216. return;
  217. }
  218. {
  219. SIZER_COMPONENT_NAME(pSizer, "CrySystem");
  220. {
  221. pSizer->AddObject(this, sizeof(*this));
  222. {
  223. //SIZER_COMPONENT_NAME (pSizer, "$Allocations waste");
  224. //const SmallModuleInfo* info = FindModuleInfo(stats, "CrySystem.dll");
  225. //if (info)
  226. //pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested );
  227. }
  228. {
  229. SIZER_COMPONENT_NAME(pSizer, "VFS");
  230. if (m_pStreamEngine)
  231. {
  232. SIZER_COMPONENT_NAME(pSizer, "Stream Engine");
  233. m_pStreamEngine->GetMemoryStatistics(pSizer);
  234. }
  235. }
  236. {
  237. SIZER_COMPONENT_NAME(pSizer, "Localization Data");
  238. m_pLocalizationManager->GetMemoryUsage(pSizer);
  239. }
  240. {
  241. SIZER_COMPONENT_NAME(pSizer, "XML");
  242. m_pXMLUtils->GetMemoryUsage(pSizer);
  243. }
  244. if (m_env.pConsole)
  245. {
  246. SIZER_COMPONENT_NAME (pSizer, "Console");
  247. m_env.pConsole->GetMemoryUsage (pSizer);
  248. }
  249. if (m_env.pLog)
  250. {
  251. SIZER_COMPONENT_NAME (pSizer, "Log");
  252. m_env.pLog->GetMemoryUsage(pSizer);
  253. }
  254. }
  255. }
  256. if (m_env.p3DEngine)
  257. {
  258. SIZER_COMPONENT_NAME(pSizer, "Cry3DEngine");
  259. {
  260. m_env.p3DEngine->GetMemoryUsage (pSizer);
  261. {
  262. SIZER_COMPONENT_NAME (pSizer, "$Allocations waste");
  263. const SmallModuleInfo* info = FindModuleInfo(stats, "Cry3DEngine.dll");
  264. if (info)
  265. {
  266. pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested);
  267. }
  268. }
  269. }
  270. }
  271. if (m_env.pRenderer)
  272. {
  273. SIZER_COMPONENT_NAME(pSizer, "CryRenderer");
  274. {
  275. {
  276. SIZER_COMPONENT_NAME (pSizer, "$Allocations waste D3D9");
  277. const SmallModuleInfo* info = FindModuleInfo(stats, "CryRenderD3D9.dll");
  278. if (info)
  279. {
  280. pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested);
  281. }
  282. }
  283. {
  284. SIZER_COMPONENT_NAME (pSizer, "$Allocations waste D3D10");
  285. const SmallModuleInfo* info = FindModuleInfo(stats, "CryRenderD3D10.dll");
  286. if (info)
  287. {
  288. pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested);
  289. }
  290. }
  291. m_env.pRenderer->GetMemoryUsage(pSizer);
  292. }
  293. }
  294. if (m_env.pCryFont)
  295. {
  296. SIZER_COMPONENT_NAME(pSizer, "CryFont");
  297. {
  298. {
  299. SIZER_COMPONENT_NAME (pSizer, "$Allocations waste");
  300. const SmallModuleInfo* info = FindModuleInfo(stats, "CryFont.dll");
  301. if (info)
  302. {
  303. pSizer->AddObject(info, info->memInfo.allocated - info->memInfo.requested);
  304. }
  305. }
  306. m_env.pCryFont->GetMemoryUsage(pSizer);
  307. // m_pIFont and m_pIFontUi are both counted in pCryFont sizing if they exist.
  308. // no need to manually add them here.
  309. }
  310. }
  311. {
  312. SIZER_COMPONENT_NAME(pSizer, "UserData");
  313. if (m_pUserCallback)
  314. {
  315. m_pUserCallback->GetMemoryUsage(pSizer);
  316. }
  317. }
  318. #ifdef WIN32
  319. {
  320. SIZER_COMPONENT_NAME(pSizer, "Code");
  321. GetExeSizes (pSizer, nPurpose);
  322. }
  323. #endif
  324. pSizer->End();
  325. }
  326. //////////////////////////////////////////////////////////////////////////
  327. const char* CSystem::GetUserName()
  328. {
  329. #if defined(WIN32) || defined(WIN64)
  330. static const int iNameBufferSize = 1024;
  331. static char szNameBuffer[iNameBufferSize];
  332. memset(szNameBuffer, 0, iNameBufferSize);
  333. DWORD dwSize = iNameBufferSize;
  334. wchar_t nameW[iNameBufferSize];
  335. ::GetUserNameW(nameW, &dwSize);
  336. cry_strcpy(szNameBuffer, CryStringUtils::WStrToUTF8(nameW));
  337. return szNameBuffer;
  338. #else
  339. #if defined(LINUX)
  340. static uid_t uid = geteuid ();
  341. static struct passwd* pw = getpwuid (uid);
  342. if (pw)
  343. {
  344. return (pw->pw_name);
  345. }
  346. else
  347. {
  348. return NULL;
  349. }
  350. #elif defined(APPLE)
  351. static const int iNameBufferSize = 1024;
  352. static char szNameBuffer[iNameBufferSize];
  353. if(SystemUtilsApple::GetUserName(szNameBuffer, iNameBufferSize))
  354. {
  355. return szNameBuffer;
  356. }
  357. else
  358. {
  359. return "";
  360. }
  361. #else
  362. return "";
  363. #endif
  364. #endif
  365. }
  366. //////////////////////////////////////////////////////////////////////////
  367. int CSystem::GetApplicationInstance()
  368. {
  369. #ifdef WIN32
  370. // tools that declare themselves as in "tool mode" may not access @user@ and may also not lock it
  371. if (gEnv->IsInToolMode())
  372. {
  373. return 0;
  374. }
  375. // this code below essentially "locks" an instance of the USER folder to a specific running application
  376. if (m_iApplicationInstance == -1)
  377. {
  378. string suffix;
  379. for (int instance = 0;; ++instance)
  380. {
  381. suffix.Format("(%d)", instance);
  382. HANDLE instanceMutex = CreateMutex(NULL, TRUE, "LumberyardApplication" + suffix);
  383. // search for duplicates
  384. if (GetLastError() != ERROR_ALREADY_EXISTS)
  385. {
  386. m_iApplicationInstance = instance;
  387. break;
  388. }
  389. }
  390. }
  391. return m_iApplicationInstance;
  392. #else
  393. return 0;
  394. #endif
  395. }
  396. int CSystem::GetApplicationLogInstance([[maybe_unused]] const char* logFilePath)
  397. {
  398. #if AZ_TRAIT_OS_USE_WINDOWS_MUTEX
  399. string suffix;
  400. int instance = 0;
  401. for (;; ++instance)
  402. {
  403. suffix.Format("(%d)", instance);
  404. HANDLE instanceMutex = CreateMutex(NULL, TRUE, logFilePath + suffix);
  405. if (GetLastError() != ERROR_ALREADY_EXISTS)
  406. {
  407. break;
  408. }
  409. }
  410. return instance;
  411. #else
  412. return 0;
  413. #endif
  414. }
  415. // refreshes the m_pMemStats if necessary; creates it if it's not created
  416. //////////////////////////////////////////////////////////////////////////
  417. void CSystem::TickMemStats(MemStatsPurposeEnum nPurpose, IResourceCollector* pResourceCollector)
  418. {
  419. // gather the statistics, if required
  420. // if there's no object, or if it's time to recalculate, or if it's for dump, then recalculate it
  421. if (!m_pMemStats || (m_env.pRenderer->GetFrameID(false) % m_cvMemStats->GetIVal()) == 0 || nPurpose == nMSP_ForDump)
  422. {
  423. if (!m_pMemStats)
  424. {
  425. if (m_cvMemStats->GetIVal() < 4 && m_cvMemStats->GetIVal())
  426. {
  427. GetILog()->LogToConsole("memstats is too small (%d). Performance impact can be significant. Please set to a greater value.", m_cvMemStats->GetIVal());
  428. }
  429. m_pMemStats = new CrySizerStats();
  430. }
  431. if (!m_pSizer)
  432. {
  433. m_pSizer = new CrySizerImpl();
  434. }
  435. m_pSizer->SetResourceCollector(pResourceCollector);
  436. m_pMemStats->startTimer(0, GetITimer());
  437. CollectMemStats (m_pSizer, nPurpose);
  438. m_pMemStats->stopTimer(0, GetITimer());
  439. m_pMemStats->startTimer(1, GetITimer());
  440. CrySizerStatsBuilder builder (m_pSizer);
  441. builder.build (m_pMemStats);
  442. m_pMemStats->stopTimer(1, GetITimer());
  443. m_pMemStats->startTimer(2, GetITimer());
  444. m_pSizer->clear();
  445. m_pMemStats->stopTimer(2, GetITimer());
  446. }
  447. else
  448. {
  449. m_pMemStats->incAgeFrames();
  450. }
  451. }
  452. //#define __HASXP
  453. // these 2 functions are duplicated in System.cpp in editor
  454. //////////////////////////////////////////////////////////////////////////
  455. #if !defined(LINUX)
  456. extern int CryStats(char* buf);
  457. #endif
  458. int CSystem::DumpMMStats(bool log)
  459. {
  460. #if defined(LINUX)
  461. return 0;
  462. #else
  463. if (log)
  464. {
  465. char buf[1024];
  466. int n = CryStats(buf);
  467. GetILog()->Log(buf);
  468. return n;
  469. }
  470. else
  471. {
  472. return CryStats(NULL);
  473. };
  474. #endif
  475. };
  476. //////////////////////////////////////////////////////////////////////////
  477. struct CryDbgModule
  478. {
  479. HANDLE heap;
  480. WIN_HMODULE handle;
  481. string name;
  482. DWORD dwSize;
  483. };
  484. //////////////////////////////////////////////////////////////////////////
  485. void CSystem::DebugStats([[maybe_unused]] bool checkpoint, [[maybe_unused]] bool leaks)
  486. {
  487. #ifdef WIN32
  488. std::vector<CryDbgModule> dbgmodules;
  489. //////////////////////////////////////////////////////////////////////////
  490. // Use windows Performance Monitoring API to enumerate all modules of current process.
  491. //////////////////////////////////////////////////////////////////////////
  492. HANDLE hSnapshot;
  493. hSnapshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0);
  494. if (hSnapshot != INVALID_HANDLE_VALUE)
  495. {
  496. MODULEENTRY32 me;
  497. memset (&me, 0, sizeof(me));
  498. me.dwSize = sizeof(me);
  499. if (Module32First (hSnapshot, &me))
  500. {
  501. // the sizes of each module group
  502. do
  503. {
  504. CryDbgModule module;
  505. module.handle = me.hModule;
  506. module.name = me.szModule;
  507. module.dwSize = me.modBaseSize;
  508. dbgmodules.push_back(module);
  509. } while (Module32Next(hSnapshot, &me));
  510. }
  511. CloseHandle (hSnapshot);
  512. }
  513. //////////////////////////////////////////////////////////////////////////
  514. ILog* log = GetILog();
  515. int totalal = 0, totalbl = 0, nolib = 0;
  516. #ifdef _DEBUG
  517. int extrastats[10];
  518. #endif
  519. int totalUsedInModules = 0;
  520. int countedMemoryModules = 0;
  521. for (int i = 0; i < (int)(dbgmodules.size()); i++)
  522. {
  523. if (!dbgmodules[i].handle)
  524. {
  525. CryLogAlways("WARNING: <CrySystem> CSystem::DebugStats: NULL handle for %s", dbgmodules[i].name.c_str());
  526. nolib++;
  527. continue;
  528. }
  529. ;
  530. typedef int (* PFN_MODULEMEMORY)();
  531. PFN_MODULEMEMORY fpCryModuleGetAllocatedMemory = (PFN_MODULEMEMORY)::GetProcAddress((HMODULE)dbgmodules[i].handle, "CryModuleGetAllocatedMemory");
  532. if (fpCryModuleGetAllocatedMemory)
  533. {
  534. int allocatedMemory = fpCryModuleGetAllocatedMemory();
  535. totalUsedInModules += allocatedMemory;
  536. countedMemoryModules++;
  537. CryLogAlways("%8d K used in Module %s: ", allocatedMemory / 1024, dbgmodules[i].name.c_str());
  538. }
  539. #ifdef _DEBUG
  540. typedef void (* PFNUSAGESUMMARY)(ILog* log, const char*, int*);
  541. typedef void (* PFNCHECKPOINT)();
  542. PFNUSAGESUMMARY fpu = (PFNUSAGESUMMARY)::GetProcAddress((HMODULE)dbgmodules[i].handle, "UsageSummary");
  543. PFNCHECKPOINT fpc = (PFNCHECKPOINT)::GetProcAddress((HMODULE)dbgmodules[i].handle, "CheckPoint");
  544. if (fpu && fpc)
  545. {
  546. if (checkpoint)
  547. {
  548. fpc();
  549. }
  550. else
  551. {
  552. extrastats[2] = (int)leaks;
  553. fpu(log, dbgmodules[i].name.c_str(), extrastats);
  554. totalal += extrastats[0];
  555. totalbl += extrastats[1];
  556. };
  557. }
  558. else
  559. {
  560. CryLogAlways("WARNING: <CrySystem> CSystem::DebugStats: could not retrieve function from DLL %s", dbgmodules[i].name.c_str());
  561. nolib++;
  562. };
  563. #endif
  564. typedef HANDLE(* PFNGETDLLHEAP)();
  565. PFNGETDLLHEAP fpg = (PFNGETDLLHEAP)::GetProcAddress((HMODULE)dbgmodules[i].handle, "GetDLLHeap");
  566. if (fpg)
  567. {
  568. dbgmodules[i].heap = fpg();
  569. }
  570. ;
  571. }
  572. ;
  573. CryLogAlways("-------------------------------------------------------");
  574. CryLogAlways("%8d K Total Memory Allocated in %d Modules", totalUsedInModules / 1024, countedMemoryModules);
  575. #ifdef _DEBUG
  576. CryLogAlways("$8GRAND TOTAL: %d k, %d blocks (%d dlls not included)", totalal / 1024, totalbl, nolib);
  577. CryLogAlways("estimated debugalloc overhead: between %d k and %d k", totalbl * 36 / 1024, totalbl * 72 / 1024);
  578. #endif
  579. //////////////////////////////////////////////////////////////////////////
  580. // Get HeapQueryInformation pointer if on windows XP.
  581. //////////////////////////////////////////////////////////////////////////
  582. typedef BOOL (WINAPI * FUNC_HeapQueryInformation)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T);
  583. FUNC_HeapQueryInformation pFnHeapQueryInformation = NULL;
  584. HMODULE hKernelInstance = CryLoadLibrary("Kernel32.dll");
  585. if (hKernelInstance)
  586. {
  587. pFnHeapQueryInformation = (FUNC_HeapQueryInformation)(::GetProcAddress(hKernelInstance, "HeapQueryInformation"));
  588. }
  589. //////////////////////////////////////////////////////////////////////////
  590. const int MAXHANDLES = 100;
  591. HANDLE handles[MAXHANDLES];
  592. int realnumh = GetProcessHeaps(MAXHANDLES, handles);
  593. char hinfo[1024];
  594. PROCESS_HEAP_ENTRY phe;
  595. CryLogAlways("$6--------------------- dump of windows heaps ---------------------");
  596. int nTotalC = 0, nTotalCP = 0, nTotalUC = 0, nTotalUCP = 0, totalo = 0;
  597. for (int i = 0; i < realnumh; i++)
  598. {
  599. HANDLE hHeap = handles[i];
  600. HeapCompact(hHeap, 0);
  601. hinfo[0] = 0;
  602. if (pFnHeapQueryInformation)
  603. {
  604. pFnHeapQueryInformation(hHeap, HeapCompatibilityInformation, hinfo, 1024, NULL);
  605. }
  606. else
  607. {
  608. for (int m = 0; m < (int)(dbgmodules.size()); m++)
  609. {
  610. if (dbgmodules[m].heap == handles[i])
  611. {
  612. azstrcpy(hinfo, AZ_ARRAY_SIZE(hinfo), dbgmodules[m].name.c_str());
  613. }
  614. }
  615. }
  616. phe.lpData = NULL;
  617. int nCommitted = 0, nUncommitted = 0, nOverhead = 0;
  618. int nCommittedPieces = 0, nUncommittedPieces = 0;
  619. int nPrevRegionIndex = -1;
  620. while (HeapWalk(hHeap, &phe))
  621. {
  622. if (phe.wFlags & PROCESS_HEAP_REGION)
  623. {
  624. assert (++nPrevRegionIndex == phe.iRegionIndex);
  625. nCommitted += phe.Region.dwCommittedSize;
  626. nUncommitted += phe.Region.dwUnCommittedSize;
  627. assert (phe.cbData == 0 || (phe.wFlags & PROCESS_HEAP_ENTRY_BUSY));
  628. }
  629. else
  630. if (phe.wFlags & PROCESS_HEAP_UNCOMMITTED_RANGE)
  631. {
  632. nUncommittedPieces += phe.cbData;
  633. }
  634. else
  635. {
  636. //if (phe.wFlags & PROCESS_HEAP_ENTRY_BUSY)
  637. nCommittedPieces += phe.cbData;
  638. }
  639. {
  640. /*
  641. MEMORY_BASIC_INFORMATION mbi;
  642. if (VirtualQuery(phe.lpData, &mbi,sizeof(mbi)) == sizeof(mbi))
  643. {
  644. if (mbi.State == MEM_COMMIT)
  645. nCommittedPieces += phe.cbData;//mbi.RegionSize;
  646. //else
  647. // nUncommitted += mbi.RegionSize;
  648. }
  649. else
  650. nCommittedPieces += phe.cbData;
  651. */
  652. }
  653. nOverhead += phe.cbOverhead;
  654. }
  655. ;
  656. int nCommittedMin = min(nCommitted, nCommittedPieces);
  657. int nCommittedMax = max(nCommitted, nCommittedPieces);
  658. CryLogAlways("* heap %8x: %6d (or ~%6d) K in use, %6d..%6d K uncommitted, %6d K overhead (%s)\n",
  659. handles[i], nCommittedPieces / 1024, nCommitted / 1024, nUncommittedPieces / 1024, nUncommitted / 1024, nOverhead / 1024, hinfo);
  660. nTotalC += nCommitted;
  661. nTotalCP += nCommittedPieces;
  662. nTotalUC += nUncommitted;
  663. nTotalUCP += nUncommittedPieces;
  664. totalo += nOverhead;
  665. }
  666. ;
  667. 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);
  668. #endif //WIN32
  669. };
  670. #ifdef WIN32
  671. struct DumpHeap32Stats
  672. {
  673. DumpHeap32Stats()
  674. : dwFree(0)
  675. , dwMoveable(0)
  676. , dwFixed(0)
  677. , dwUnknown(0)
  678. {
  679. }
  680. void operator += (const DumpHeap32Stats& right)
  681. {
  682. dwFree += right.dwFree;
  683. dwMoveable += right.dwMoveable;
  684. dwFixed += right.dwFixed;
  685. dwUnknown += right.dwUnknown;
  686. }
  687. DWORD dwFree;
  688. DWORD dwMoveable;
  689. DWORD dwFixed;
  690. DWORD dwUnknown;
  691. };
  692. static void DumpHeap32 (const HEAPLIST32& hl, DumpHeap32Stats& stats)
  693. {
  694. HEAPENTRY32 he;
  695. memset (&he, 0, sizeof(he));
  696. he.dwSize = sizeof(he);
  697. if (Heap32First (&he, hl.th32ProcessID, hl.th32HeapID))
  698. {
  699. DumpHeap32Stats heap;
  700. do
  701. {
  702. if (he.dwFlags & LF32_FREE)
  703. {
  704. heap.dwFree += he.dwBlockSize;
  705. }
  706. else
  707. if (he.dwFlags & LF32_MOVEABLE)
  708. {
  709. heap.dwMoveable += he.dwBlockSize;
  710. }
  711. else
  712. if (he.dwFlags & LF32_FIXED)
  713. {
  714. heap.dwFixed += he.dwBlockSize;
  715. }
  716. else
  717. {
  718. heap.dwUnknown += he.dwBlockSize;
  719. }
  720. } while (Heap32Next (&he));
  721. CryLogAlways ("%08X %6d %6d %6d (%d)", hl.th32HeapID, heap.dwFixed / 0x400, heap.dwFree / 0x400, heap.dwMoveable / 0x400, heap.dwUnknown / 0x400);
  722. stats += heap;
  723. }
  724. else
  725. {
  726. CryLogAlways ("%08X empty or invalid");
  727. }
  728. }
  729. //////////////////////////////////////////////////////////////////////////
  730. class CStringOrder
  731. {
  732. public:
  733. bool operator () (const char* szLeft, const char* szRight) const {return azstricmp(szLeft, szRight) < 0; }
  734. };
  735. typedef std::map<const char*, unsigned, CStringOrder> StringToSizeMap;
  736. void AddSize (StringToSizeMap& mapSS, const char* szString, unsigned nSize)
  737. {
  738. StringToSizeMap::iterator it = mapSS.find (szString);
  739. if (it == mapSS.end())
  740. {
  741. mapSS.insert (StringToSizeMap::value_type(szString, nSize));
  742. }
  743. else
  744. {
  745. it->second += nSize;
  746. }
  747. }
  748. //////////////////////////////////////////////////////////////////////////
  749. const char* GetModuleGroup (const char* szString)
  750. {
  751. for (unsigned i = 0; i < sizeof(g_szModuleGroups) / sizeof(g_szModuleGroups[0]); ++i)
  752. {
  753. if (azstricmp(szString, g_szModuleGroups[i][0]) == 0)
  754. {
  755. return g_szModuleGroups[i][1];
  756. }
  757. }
  758. return "Other";
  759. }
  760. //////////////////////////////////////////////////////////////////////////
  761. void CSystem::GetExeSizes (ICrySizer* pSizer, MemStatsPurposeEnum nPurpose)
  762. {
  763. HANDLE hSnapshot;
  764. hSnapshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0);
  765. if (hSnapshot == INVALID_HANDLE_VALUE)
  766. {
  767. CryLogAlways ("Cannot get the module snapshot, error code %d", GetLastError());
  768. return;
  769. }
  770. DWORD dwProcessID = GetCurrentProcessId();
  771. MODULEENTRY32 me;
  772. memset (&me, 0, sizeof(me));
  773. me.dwSize = sizeof(me);
  774. if (Module32First (hSnapshot, &me))
  775. {
  776. // the sizes of each module group
  777. StringToSizeMap mapGroupSize;
  778. DWORD dwTotalModuleSize = 0;
  779. do
  780. {
  781. dwProcessID = me.th32ProcessID;
  782. const char* szGroup = GetModuleGroup (me.szModule);
  783. SIZER_COMPONENT_NAME(pSizer, szGroup);
  784. if (nPurpose == nMSP_ForDump)
  785. {
  786. SIZER_COMPONENT_NAME(pSizer, me.szModule);
  787. pSizer->AddObject(me.modBaseAddr, me.modBaseSize);
  788. }
  789. else
  790. {
  791. pSizer->AddObject(me.modBaseAddr, me.modBaseSize);
  792. }
  793. } while (Module32Next(hSnapshot, &me));
  794. }
  795. else
  796. {
  797. CryLogAlways ("No modules to dump");
  798. }
  799. CloseHandle (hSnapshot);
  800. }
  801. #endif
  802. //////////////////////////////////////////////////////////////////////////
  803. void CSystem::DumpWinHeaps()
  804. {
  805. #ifdef WIN32
  806. //
  807. // Retrieve modules and log them; remember the process id
  808. HANDLE hSnapshot;
  809. hSnapshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0);
  810. if (hSnapshot == INVALID_HANDLE_VALUE)
  811. {
  812. CryLogAlways ("Cannot get the module snapshot, error code %d", GetLastError());
  813. return;
  814. }
  815. DWORD dwProcessID = GetCurrentProcessId();
  816. MODULEENTRY32 me;
  817. memset (&me, 0, sizeof(me));
  818. me.dwSize = sizeof(me);
  819. if (Module32First (hSnapshot, &me))
  820. {
  821. // the sizes of each module group
  822. StringToSizeMap mapGroupSize;
  823. DWORD dwTotalModuleSize = 0;
  824. CryLogAlways ("base size module");
  825. do
  826. {
  827. dwProcessID = me.th32ProcessID;
  828. const char* szGroup = GetModuleGroup (me.szModule);
  829. CryLogAlways ("%08X %8X %25s - %s", me.modBaseAddr, me.modBaseSize, me.szModule, azstricmp(szGroup, "Other") ? szGroup : "");
  830. dwTotalModuleSize += me.modBaseSize;
  831. AddSize (mapGroupSize, szGroup, me.modBaseSize);
  832. } while (Module32Next(hSnapshot, &me));
  833. CryLogAlways ("------------------------------------");
  834. for (StringToSizeMap::iterator it = mapGroupSize.begin(); it != mapGroupSize.end(); ++it)
  835. {
  836. CryLogAlways (" %6.3f Mbytes - %s", double(it->second) / 0x100000, it->first);
  837. }
  838. CryLogAlways ("------------------------------------");
  839. CryLogAlways (" %6.3f Mbytes - TOTAL", double(dwTotalModuleSize) / 0x100000);
  840. CryLogAlways ("------------------------------------");
  841. }
  842. else
  843. {
  844. CryLogAlways ("No modules to dump");
  845. }
  846. CloseHandle (hSnapshot);
  847. //
  848. // Retrieve the heaps and dump each of them with a special function
  849. hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, 0);
  850. if (hSnapshot == INVALID_HANDLE_VALUE)
  851. {
  852. CryLogAlways ("Cannot get the heap LIST snapshot, error code %d", GetLastError());
  853. return;
  854. }
  855. HEAPLIST32 hl;
  856. memset (&hl, 0, sizeof(hl));
  857. hl.dwSize = sizeof(hl);
  858. CryLogAlways ("__Heap__ fixed free move (unknown)");
  859. if (Heap32ListFirst (hSnapshot, &hl))
  860. {
  861. DumpHeap32Stats stats;
  862. do
  863. {
  864. DumpHeap32 (hl, stats);
  865. } while (Heap32ListNext (hSnapshot, &hl));
  866. CryLogAlways ("-------------------------------------------------");
  867. 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);
  868. CryLogAlways ("-------------------------------------------------");
  869. }
  870. else
  871. {
  872. CryLogAlways ("No heaps to dump");
  873. }
  874. CloseHandle(hSnapshot);
  875. #endif
  876. }
  877. // Make system error message string
  878. //////////////////////////////////////////////////////////////////////////
  879. //! \return pointer to the null terminated error string or 0
  880. static const char* GetLastSystemErrorMessage()
  881. {
  882. #ifdef WIN32
  883. DWORD dwError = GetLastError();
  884. static char szBuffer[512]; // function will return pointer to this buffer
  885. if (dwError)
  886. {
  887. LPVOID lpMsgBuf = 0;
  888. if (FormatMessage(
  889. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  890. FORMAT_MESSAGE_FROM_SYSTEM |
  891. FORMAT_MESSAGE_IGNORE_INSERTS,
  892. NULL,
  893. GetLastError(),
  894. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  895. (LPTSTR) &lpMsgBuf,
  896. 0,
  897. NULL))
  898. {
  899. cry_strcpy(szBuffer, (char*)lpMsgBuf);
  900. LocalFree(lpMsgBuf);
  901. }
  902. else
  903. {
  904. return 0;
  905. }
  906. return szBuffer;
  907. }
  908. #else
  909. return 0;
  910. #endif //WIN32
  911. return 0;
  912. }
  913. //////////////////////////////////////////////////////////////////////////
  914. void CSystem::FatalError(const char* format, ...)
  915. {
  916. // Guard against reentrancy - out of memory fatal errors can become reentrant since logging can try to alloc.
  917. static bool currentlyReportingError = false;
  918. if (currentlyReportingError == true)
  919. {
  920. return;
  921. }
  922. currentlyReportingError = true;
  923. // format message
  924. va_list ArgList;
  925. char szBuffer[MAX_WARNING_LENGTH];
  926. const char* sPrefix = "";
  927. azstrcpy(szBuffer, MAX_WARNING_LENGTH, sPrefix);
  928. va_start(ArgList, format);
  929. azvsnprintf(szBuffer + strlen(sPrefix), MAX_WARNING_LENGTH - strlen(sPrefix), format, ArgList);
  930. va_end(ArgList);
  931. // get system error message before any attempt to write into log
  932. const char* szSysErrorMessage = GetLastSystemErrorMessage();
  933. CryLogAlways("=============================================================================");
  934. CryLogAlways("*ERROR");
  935. CryLogAlways("=============================================================================");
  936. // write both messages into log
  937. CryLogAlways("%s", szBuffer);
  938. if (szSysErrorMessage)
  939. {
  940. CryLogAlways("<CrySystem> Last System Error: %s", szSysErrorMessage);
  941. }
  942. if (GetUserCallback())
  943. {
  944. GetUserCallback()->OnError(szBuffer);
  945. }
  946. assert(szBuffer[0] >= ' ');
  947. // strcpy(szBuffer,szBuffer+1); // remove verbosity tag since it is not supported by ::MessageBox
  948. LogSystemInfo();
  949. CollectMemStats(0, nMSP_ForCrashLog);
  950. OutputDebugString(szBuffer);
  951. #ifdef WIN32
  952. OnFatalError(szBuffer);
  953. if (!g_cvars.sys_no_crash_dialog)
  954. {
  955. ICVar* pFullscreen = (gEnv && gEnv->pConsole) ? gEnv->pConsole->GetCVar("r_Fullscreen") : 0;
  956. if (pFullscreen && pFullscreen->GetIVal() != 0 && gEnv->pRenderer && gEnv->pRenderer->GetHWND())
  957. {
  958. ::ShowWindow((HWND)gEnv->pRenderer->GetHWND(), SW_MINIMIZE);
  959. }
  960. ::MessageBox(NULL, szBuffer, "Lumberyard Error", MB_OK | MB_ICONERROR | MB_SYSTEMMODAL);
  961. }
  962. // Dump callstack.
  963. #endif
  964. #if defined (WIN32)
  965. //Triggers a fatal error, so the DebugCallstack can create the error.log and terminate the application
  966. IDebugCallStack::instance()->FatalError(szBuffer);
  967. #elif defined(AZ_RESTRICTED_PLATFORM)
  968. #define AZ_RESTRICTED_SECTION SYSTEMWIN32_CPP_SECTION_1
  969. #include AZ_RESTRICTED_FILE(SystemWin32_cpp)
  970. #endif
  971. CryDebugBreak();
  972. // app can not continue
  973. #ifdef _DEBUG
  974. #if defined(WIN32) && !defined(WIN64)
  975. DEBUG_BREAK;
  976. #endif
  977. #else
  978. #if defined(WIN32) || defined(WIN64)
  979. _flushall();
  980. // on windows, _exit does all sorts of things which can cause cleanup to fail during a crash, we need to terminate instead.
  981. TerminateProcess(GetCurrentProcess(), 1);
  982. #endif
  983. #if defined(AZ_RESTRICTED_PLATFORM)
  984. #define AZ_RESTRICTED_SECTION SYSTEMWIN32_CPP_SECTION_2
  985. #include AZ_RESTRICTED_FILE(SystemWin32_cpp)
  986. #endif
  987. #if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
  988. #undef AZ_RESTRICTED_SECTION_IMPLEMENTED
  989. #else
  990. _exit(1);
  991. #endif
  992. #endif
  993. }
  994. void CSystem::ReportBug([[maybe_unused]] const char* format, ...)
  995. {
  996. #if defined (WIN32)
  997. va_list ArgList;
  998. char szBuffer[MAX_WARNING_LENGTH];
  999. const char* sPrefix = "";
  1000. azstrcpy(szBuffer, MAX_WARNING_LENGTH, sPrefix);
  1001. va_start(ArgList, format);
  1002. azvsnprintf(szBuffer + strlen(sPrefix), MAX_WARNING_LENGTH - strlen(sPrefix), format, ArgList);
  1003. va_end(ArgList);
  1004. IDebugCallStack::instance()->ReportBug(szBuffer);
  1005. #endif
  1006. }
  1007. //////////////////////////////////////////////////////////////////////////
  1008. void CSystem::debug_GetCallStack(const char** pFunctions, int& nCount)
  1009. {
  1010. #if defined(WIN32)
  1011. using namespace AZ::Debug;
  1012. int nMaxCount = nCount;
  1013. StackFrame* frames = (StackFrame*)AZ_ALLOCA(sizeof(StackFrame)*nMaxCount);
  1014. unsigned int numFrames = StackRecorder::Record(frames, nMaxCount, 1);
  1015. SymbolStorage::StackLine* textLines = (SymbolStorage::StackLine*)AZ_ALLOCA(sizeof(SymbolStorage::StackLine)*nMaxCount);
  1016. SymbolStorage::DecodeFrames(frames, numFrames, textLines);
  1017. for (int i = 0; i < numFrames; i++)
  1018. {
  1019. pFunctions[i] = textLines[i];
  1020. }
  1021. nCount = numFrames;
  1022. #define AZ_RESTRICTED_SECTION_IMPLEMENTED
  1023. #elif defined(AZ_RESTRICTED_PLATFORM)
  1024. #define AZ_RESTRICTED_SECTION SYSTEMWIN32_CPP_SECTION_3
  1025. #include AZ_RESTRICTED_FILE(SystemWin32_cpp)
  1026. #endif
  1027. #if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
  1028. #undef AZ_RESTRICTED_SECTION_IMPLEMENTED
  1029. #else
  1030. AZ_UNUSED(pFunctions);
  1031. nCount = 0;
  1032. #endif
  1033. }
  1034. //////////////////////////////////////////////////////////////////////////
  1035. void CSystem::debug_LogCallStack(int nMaxFuncs, [[maybe_unused]] int nFlags)
  1036. {
  1037. if (nMaxFuncs > 32)
  1038. {
  1039. nMaxFuncs = 32;
  1040. }
  1041. // Print call stack for each find.
  1042. const char* funcs[32];
  1043. int nCount = nMaxFuncs;
  1044. int nCurFrame = 0;
  1045. if (m_env.pRenderer)
  1046. {
  1047. nCurFrame = (int)m_env.pRenderer->GetFrameID(false);
  1048. }
  1049. CryLogAlways(" ----- CallStack (Frame: %d) -----", nCurFrame);
  1050. GetISystem()->debug_GetCallStack(funcs, nCount);
  1051. for (int i = 1; i < nCount; i++) // start from 1 to skip this function.
  1052. {
  1053. CryLogAlways(" %02d) %s", i, funcs[i]);
  1054. }
  1055. }
  1056. //////////////////////////////////////////////////////////////////////////
  1057. // Support relaunching for windows media center edition.
  1058. //////////////////////////////////////////////////////////////////////////
  1059. #if defined(WIN32)
  1060. #if (_WIN32_WINNT < 0x0501)
  1061. #define SM_MEDIACENTER 87
  1062. #endif
  1063. bool CSystem::ReLaunchMediaCenter()
  1064. {
  1065. // Skip if not running on a Media Center
  1066. if (GetSystemMetrics(SM_MEDIACENTER) == 0)
  1067. {
  1068. return false;
  1069. }
  1070. // Get the path to Media Center
  1071. char szExpandedPath[AZ_MAX_PATH_LEN];
  1072. if (!ExpandEnvironmentStrings("%SystemRoot%\\ehome\\ehshell.exe", szExpandedPath, AZ_MAX_PATH_LEN))
  1073. {
  1074. return false;
  1075. }
  1076. // Skip if ehshell.exe doesn't exist
  1077. if (GetFileAttributes(szExpandedPath) == 0xFFFFFFFF)
  1078. {
  1079. return false;
  1080. }
  1081. // Launch ehshell.exe
  1082. INT_PTR result = (INT_PTR)ShellExecute(NULL, TEXT("open"), szExpandedPath, NULL, NULL, SW_SHOWNORMAL);
  1083. return (result > 32);
  1084. }
  1085. #else
  1086. bool CSystem::ReLaunchMediaCenter()
  1087. {
  1088. return false;
  1089. }
  1090. #endif //defined(WIN32)
  1091. //////////////////////////////////////////////////////////////////////////
  1092. #if defined(WIN32)
  1093. void CSystem::LogSystemInfo()
  1094. {
  1095. //////////////////////////////////////////////////////////////////////
  1096. // Write the system informations to the log
  1097. //////////////////////////////////////////////////////////////////////
  1098. char szBuffer[1024];
  1099. char szProfileBuffer[128];
  1100. char szLanguageBuffer[64];
  1101. //char szCPUModel[64];
  1102. char* pChar = 0;
  1103. MEMORYSTATUSEX MemoryStatus;
  1104. MemoryStatus.dwLength = sizeof(MemoryStatus);
  1105. DEVMODE DisplayConfig;
  1106. OSVERSIONINFO OSVerInfo;
  1107. OSVerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  1108. // log Windows type
  1109. Win32SysInspect::GetOS(m_env.pi.winVer, m_env.pi.win64Bit, szBuffer, sizeof(szBuffer));
  1110. CryLogAlways(szBuffer);
  1111. // log system language
  1112. GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_SENGLANGUAGE, szLanguageBuffer, sizeof(szLanguageBuffer));
  1113. azsprintf(szBuffer, "System language: %s", szLanguageBuffer);
  1114. CryLogAlways(szBuffer);
  1115. // log Windows directory
  1116. GetWindowsDirectory(szBuffer, sizeof(szBuffer));
  1117. string str = "Windows Directory: \"";
  1118. str += szBuffer;
  1119. str += "\"";
  1120. CryLogAlways(str);
  1121. //////////////////////////////////////////////////////////////////////
  1122. // Send system time & date
  1123. //////////////////////////////////////////////////////////////////////
  1124. str = "Local time is ";
  1125. azstrtime(szBuffer);
  1126. str += szBuffer;
  1127. str += " ";
  1128. _strdate_s(szBuffer);
  1129. str += szBuffer;
  1130. azsprintf(szBuffer, ", system running for %lu minutes", GetTickCount() / 60000);
  1131. str += szBuffer;
  1132. CryLogAlways(str);
  1133. //////////////////////////////////////////////////////////////////////
  1134. // Send system memory status
  1135. //////////////////////////////////////////////////////////////////////
  1136. GlobalMemoryStatusEx(&MemoryStatus);
  1137. azsprintf(szBuffer, "%I64dMB physical memory installed, %I64dMB available, %I64dMB virtual memory installed, %ld percent of memory in use",
  1138. MemoryStatus.ullTotalPhys / 1048576 + 1,
  1139. MemoryStatus.ullAvailPhys / 1048576,
  1140. MemoryStatus.ullTotalVirtual / 1048576,
  1141. MemoryStatus.dwMemoryLoad);
  1142. CryLogAlways(szBuffer);
  1143. if (GetISystem()->GetIMemoryManager())
  1144. {
  1145. IMemoryManager::SProcessMemInfo memCounters;
  1146. GetISystem()->GetIMemoryManager()->GetProcessMemInfo(memCounters);
  1147. uint64 PagefileUsage = memCounters.PagefileUsage;
  1148. uint64 PeakPagefileUsage = memCounters.PeakPagefileUsage;
  1149. uint64 WorkingSetSize = memCounters.WorkingSetSize;
  1150. azsprintf(szBuffer, "PageFile usage: %I64dMB, Working Set: %I64dMB, Peak PageFile usage: %I64dMB,",
  1151. (uint64)PagefileUsage / (1024 * 1024),
  1152. (uint64)WorkingSetSize / (1024 * 1024),
  1153. (uint64)PeakPagefileUsage / (1024 * 1024));
  1154. CryLogAlways(szBuffer);
  1155. }
  1156. //////////////////////////////////////////////////////////////////////
  1157. // Send display settings
  1158. //////////////////////////////////////////////////////////////////////
  1159. EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &DisplayConfig);
  1160. GetPrivateProfileString("boot.description", "display.drv",
  1161. "(Unknown graphics card)", szProfileBuffer, sizeof(szProfileBuffer),
  1162. "system.ini");
  1163. azsprintf(szBuffer, "Current display mode is %lux%lux%lu, %s",
  1164. DisplayConfig.dmPelsWidth, DisplayConfig.dmPelsHeight,
  1165. DisplayConfig.dmBitsPerPel, szProfileBuffer);
  1166. CryLogAlways(szBuffer);
  1167. //////////////////////////////////////////////////////////////////////
  1168. // Send input device configuration
  1169. //////////////////////////////////////////////////////////////////////
  1170. str = "";
  1171. // Detect the keyboard type
  1172. switch (GetKeyboardType(0))
  1173. {
  1174. case 1:
  1175. str = "IBM PC/XT (83-key)";
  1176. break;
  1177. case 2:
  1178. str = "ICO (102-key)";
  1179. break;
  1180. case 3:
  1181. str = "IBM PC/AT (84-key)";
  1182. break;
  1183. case 4:
  1184. str = "IBM enhanced (101/102-key)";
  1185. break;
  1186. case 5:
  1187. str = "Nokia 1050";
  1188. break;
  1189. case 6:
  1190. str = "Nokia 9140";
  1191. break;
  1192. case 7:
  1193. str = "Japanese";
  1194. break;
  1195. default:
  1196. str = "Unknown";
  1197. break;
  1198. }
  1199. // Any mouse attached ?
  1200. if (!GetSystemMetrics(SM_MOUSEPRESENT))
  1201. {
  1202. CryLogAlways(str + " keyboard and no mouse installed");
  1203. }
  1204. else
  1205. {
  1206. azsprintf(szBuffer, " keyboard and %i+ button mouse installed",
  1207. GetSystemMetrics(SM_CMOUSEBUTTONS));
  1208. CryLogAlways(str + szBuffer);
  1209. }
  1210. CryLogAlways("--------------------------------------------------------------------------------");
  1211. }
  1212. #else
  1213. void CSystem::LogSystemInfo()
  1214. {
  1215. }
  1216. #endif
  1217. #if (defined(WIN32) || defined(WIN64))
  1218. //////////////////////////////////////////////////////////////////////////
  1219. bool CSystem::GetWinGameFolder(char* szMyDocumentsPath, int maxPathSize)
  1220. {
  1221. bool bSucceeded = false;
  1222. // check Vista and later OS first
  1223. HMODULE shell32 = LoadLibraryA("Shell32.dll");
  1224. if (shell32)
  1225. {
  1226. typedef long (__stdcall * T_SHGetKnownFolderPath)(REFKNOWNFOLDERID rfid, unsigned long dwFlags, void* hToken, wchar_t** ppszPath);
  1227. T_SHGetKnownFolderPath SHGetKnownFolderPath = (T_SHGetKnownFolderPath)GetProcAddress(shell32, "SHGetKnownFolderPath");
  1228. if (SHGetKnownFolderPath)
  1229. {
  1230. // We must be running Vista or newer
  1231. wchar_t* wMyDocumentsPath;
  1232. HRESULT hr = SHGetKnownFolderPath(FOLDERID_SavedGames, KF_FLAG_CREATE | KF_FLAG_DONT_UNEXPAND, NULL, &wMyDocumentsPath);
  1233. bSucceeded = SUCCEEDED(hr);
  1234. if (bSucceeded)
  1235. {
  1236. // Convert from UNICODE to UTF-8
  1237. cry_strcpy(szMyDocumentsPath, maxPathSize, CryStringUtils::WStrToUTF8(wMyDocumentsPath));
  1238. CoTaskMemFree(wMyDocumentsPath);
  1239. }
  1240. }
  1241. FreeLibrary(shell32);
  1242. }
  1243. if (!bSucceeded)
  1244. {
  1245. // check pre-vista OS if not succeeded before
  1246. wchar_t wMyDocumentsPath[AZ_MAX_PATH_LEN];
  1247. bSucceeded = SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, 0, wMyDocumentsPath));
  1248. if (bSucceeded)
  1249. {
  1250. cry_strcpy(szMyDocumentsPath, maxPathSize, CryStringUtils::WStrToUTF8(wMyDocumentsPath));
  1251. }
  1252. }
  1253. return bSucceeded;
  1254. }
  1255. #endif
  1256. //////////////////////////////////////////////////////////////////////////
  1257. void CSystem::DetectGameFolderAccessRights()
  1258. {
  1259. // This code is trying to figure out if the current folder we are now running under have write access.
  1260. // By default assume folder is not writable.
  1261. // If folder is writable game.log is saved there, otherwise it is saved in user documents folder.
  1262. #if defined(WIN32)
  1263. DWORD DesiredAccess = FILE_GENERIC_WRITE;
  1264. DWORD GrantedAccess = 0;
  1265. DWORD dwRes = 0;
  1266. PACL pDACL = NULL;
  1267. PSECURITY_DESCRIPTOR pSD = NULL;
  1268. HANDLE hClientToken = 0;
  1269. PRIVILEGE_SET PrivilegeSet;
  1270. DWORD PrivilegeSetLength = sizeof(PrivilegeSet);
  1271. BOOL bAccessStatus = FALSE;
  1272. // Get a pointer to the existing DACL.
  1273. dwRes = GetNamedSecurityInfo(".", SE_FILE_OBJECT,
  1274. DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION,
  1275. NULL, NULL, &pDACL, NULL, &pSD);
  1276. if (ERROR_SUCCESS != dwRes)
  1277. {
  1278. //
  1279. assert(0);
  1280. }
  1281. if (!ImpersonateSelf(SecurityIdentification))
  1282. {
  1283. return;
  1284. }
  1285. if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hClientToken) && hClientToken != 0)
  1286. {
  1287. return;
  1288. }
  1289. GENERIC_MAPPING GenMap;
  1290. GenMap.GenericRead = FILE_GENERIC_READ;
  1291. GenMap.GenericWrite = FILE_GENERIC_WRITE;
  1292. GenMap.GenericExecute = FILE_GENERIC_EXECUTE;
  1293. GenMap.GenericAll = FILE_ALL_ACCESS;
  1294. MapGenericMask(&DesiredAccess, &GenMap);
  1295. if (!AccessCheck(pSD, hClientToken, DesiredAccess, &GenMap, &PrivilegeSet, &PrivilegeSetLength, &GrantedAccess, &bAccessStatus))
  1296. {
  1297. RevertToSelf();
  1298. CloseHandle(hClientToken);
  1299. return;
  1300. }
  1301. CloseHandle(hClientToken);
  1302. RevertToSelf();
  1303. if (bAccessStatus)
  1304. {
  1305. m_bGameFolderWritable = true;
  1306. }
  1307. #elif defined(MOBILE)
  1308. char cwd[AZ_MAX_PATH_LEN];
  1309. if (getcwd(cwd, AZ_MAX_PATH_LEN) != NULL)
  1310. {
  1311. if (0 == access(cwd, W_OK))
  1312. {
  1313. m_bGameFolderWritable = true;
  1314. }
  1315. }
  1316. #endif //WIN32
  1317. }
  1318. /////////////////////////////////`/////////////////////////////////////////
  1319. void CSystem::EnableFloatExceptions([[maybe_unused]] int type)
  1320. {
  1321. #ifndef _RELEASE
  1322. #if defined(WIN32)
  1323. #if defined(WIN32) && !defined(WIN64)
  1324. // Optimization
  1325. // Enable DAZ/FZ
  1326. // Denormals Are Zeros
  1327. // Flush-to-Zero
  1328. _controlfp(_DN_FLUSH, _MCW_DN);
  1329. #endif //#if defined(WIN32) && !defined(WIN64)
  1330. AZ_PUSH_DISABLE_WARNING(4996, "-Wunknown-warning-option")
  1331. _controlfp(_DN_FLUSH, _MCW_DN);
  1332. if (type == 0)
  1333. {
  1334. // mask all floating exceptions off.
  1335. _controlfp(_EM_INEXACT | _EM_UNDERFLOW | _EM_OVERFLOW | _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE, _MCW_EM);
  1336. }
  1337. else
  1338. {
  1339. // Clear pending exceptions
  1340. _fpreset();
  1341. if (type == 1)
  1342. {
  1343. // enable just the most important fp-exceptions.
  1344. _controlfp(_EM_INEXACT | _EM_UNDERFLOW | _EM_OVERFLOW, _MCW_EM); // Enable floating point exceptions.
  1345. }
  1346. if (type == 2)
  1347. {
  1348. // enable ALL floating point exceptions.
  1349. _controlfp(_EM_INEXACT, _MCW_EM);
  1350. }
  1351. }
  1352. AZ_POP_DISABLE_WARNING
  1353. #endif //#if defined(WIN32) && !defined(WIN64)
  1354. #ifdef WIN32
  1355. _mm_setcsr(_mm_getcsr() & ~0x280 | (type > 0 ? 0 : 0x280));
  1356. #endif
  1357. #endif //_RELEASE
  1358. }