!XF (Memory) (DEV-7064) Log the callstack for very large allocations.
[CRYENGINE.git] / Code / CryEngine / CrySystem / MemoryManager.cpp
blob3ee5be5559a2f99d6af6e4c903c974bdf2e2efab
1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
3 #include "StdAfx.h"
4 #include "MemoryManager.h"
5 #include <CryCore/Platform/platform.h>
6 #include "MemReplay.h"
7 #include "CustomMemoryHeap.h"
8 #include "GeneralMemoryHeap.h"
9 #include "PageMappingHeap.h"
10 #include "DefragAllocator.h"
12 #if CRY_PLATFORM_WINDOWS
13 #include <Psapi.h>
14 #endif
16 #if CRY_PLATFORM_APPLE
17 #include <mach/mach.h> // task_info
18 #endif
20 #if CRY_PLATFORM_LINUX || CRY_PLATFORM_ANDROID || CRY_PLATFORM_APPLE
21 #include <sys/types.h> // required by mman.h
22 #include <sys/mman.h> //mmap - virtual memory manager
23 #endif
24 extern LONG g_TotalAllocatedMemory;
26 #ifdef MEMMAN_STATIC
27 CCryMemoryManager g_memoryManager;
28 #endif
30 //////////////////////////////////////////////////////////////////////////
31 CCryMemoryManager* CCryMemoryManager::GetInstance()
33 #ifdef MEMMAN_STATIC
34 return &g_memoryManager;
35 #else
36 static CCryMemoryManager memman;
37 return &memman;
38 #endif
41 #ifndef MEMMAN_STATIC
42 #include <CrySystem/IConsole.h>
44 int CCryMemoryManager::s_sys_MemoryDeadListSize;
46 void CCryMemoryManager::RegisterCVars()
48 REGISTER_CVAR2("sys_MemoryDeadListSize", &s_sys_MemoryDeadListSize, 0, VF_REQUIRE_APP_RESTART, "Keep upto size bytes in a \"deadlist\" of allocations to assist in capturing tramples");
50 #endif
52 //////////////////////////////////////////////////////////////////////////
53 bool CCryMemoryManager::GetProcessMemInfo(SProcessMemInfo& minfo)
55 ZeroStruct(minfo);
56 #if CRY_PLATFORM_WINDOWS
58 MEMORYSTATUSEX mem;
59 mem.dwLength = sizeof(mem);
60 GlobalMemoryStatusEx(&mem);
62 minfo.TotalPhysicalMemory = mem.ullTotalPhys;
63 minfo.FreePhysicalMemory = mem.ullAvailPhys;
65 //////////////////////////////////////////////////////////////////////////
66 typedef BOOL (WINAPI * GetProcessMemoryInfoProc)(HANDLE, PPROCESS_MEMORY_COUNTERS, DWORD);
68 PROCESS_MEMORY_COUNTERS pc;
69 ZeroStruct(pc);
70 pc.cb = sizeof(pc);
71 static HMODULE hPSAPI = LoadLibraryA("psapi.dll");
72 if (hPSAPI)
74 static GetProcessMemoryInfoProc pGetProcessMemoryInfo = (GetProcessMemoryInfoProc)GetProcAddress(hPSAPI, "GetProcessMemoryInfo");
75 if (pGetProcessMemoryInfo)
77 if (pGetProcessMemoryInfo(GetCurrentProcess(), &pc, sizeof(pc)))
79 minfo.PageFaultCount = pc.PageFaultCount;
80 minfo.PeakWorkingSetSize = pc.PeakWorkingSetSize;
81 minfo.WorkingSetSize = pc.WorkingSetSize;
82 minfo.QuotaPeakPagedPoolUsage = pc.QuotaPeakPagedPoolUsage;
83 minfo.QuotaPagedPoolUsage = pc.QuotaPagedPoolUsage;
84 minfo.QuotaPeakNonPagedPoolUsage = pc.QuotaPeakNonPagedPoolUsage;
85 minfo.QuotaNonPagedPoolUsage = pc.QuotaNonPagedPoolUsage;
86 minfo.PagefileUsage = pc.PagefileUsage;
87 minfo.PeakPagefileUsage = pc.PeakPagefileUsage;
89 return true;
93 return false;
95 #elif CRY_PLATFORM_ORBIS
97 size_t mainMemory, videoMemory;
98 VirtualAllocator::QueryMemory(mainMemory, videoMemory); // GlobalMemoryStatus would be more accurate but also slower
99 minfo.TotalPhysicalMemory = sceKernelGetDirectMemorySize();
100 minfo.PeakPagefileUsage = minfo.PagefileUsage = mainMemory + videoMemory;
101 minfo.FreePhysicalMemory = minfo.TotalPhysicalMemory - (mainMemory + videoMemory);
102 #elif CRY_PLATFORM_LINUX || CRY_PLATFORM_ANDROID
104 MEMORYSTATUS MemoryStatus;
105 GlobalMemoryStatus(&MemoryStatus);
106 minfo.PagefileUsage = minfo.PeakPagefileUsage = MemoryStatus.dwTotalPhys - MemoryStatus.dwAvailPhys;
108 minfo.FreePhysicalMemory = MemoryStatus.dwAvailPhys;
109 minfo.TotalPhysicalMemory = MemoryStatus.dwTotalPhys;
111 #if CRY_PLATFORM_ANDROID
112 // On Android, mallinfo() is an EXTREMELY time consuming operation. Nearly 80% CPU time will be spent
113 // on this operation once -memreplay is given. Since WorkingSetSize is only used for statistics and
114 // debugging purpose, it's simply ignored.
115 minfo.WorkingSetSize = 0;
116 #else
117 struct mallinfo meminfo = mallinfo();
118 minfo.WorkingSetSize = meminfo.usmblks + meminfo.uordblks;
119 #endif
121 #elif CRY_PLATFORM_APPLE
123 MEMORYSTATUS MemoryStatus;
124 GlobalMemoryStatus(&MemoryStatus);
125 minfo.PagefileUsage = minfo.PeakPagefileUsage = MemoryStatus.dwTotalPhys - MemoryStatus.dwAvailPhys;
127 minfo.FreePhysicalMemory = MemoryStatus.dwAvailPhys;
128 minfo.TotalPhysicalMemory = MemoryStatus.dwTotalPhys;
130 // Retrieve WorkingSetSize from task_info
131 task_basic_info kTaskInfo;
132 mach_msg_type_number_t uInfoCount(sizeof(kTaskInfo) / sizeof(natural_t));
133 if (task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&kTaskInfo, &uInfoCount) != 0)
135 gEnv->pLog->LogError("task_info failed\n");
136 return false;
138 minfo.WorkingSetSize = kTaskInfo.resident_size;
139 #elif CRY_PLATFORM_DURANGO
141 //Memory status
142 TITLEMEMORYSTATUS DurangoMemoryStatus;
143 DurangoMemoryStatus.dwLength = sizeof(DurangoMemoryStatus);
144 if (TitleMemoryStatus(&DurangoMemoryStatus) != 0)
146 uint64 titleUsed = (uint64)(DurangoMemoryStatus.ullTitleUsed);
147 uint64 legacyUsed = (uint64)(DurangoMemoryStatus.ullLegacyUsed);
148 uint64 total = (uint64)(DurangoMemoryStatus.ullTotalMem);
150 minfo.PagefileUsage = legacyUsed + titleUsed;
152 static uint64 peak = minfo.PagefileUsage;
153 if (peak < minfo.PagefileUsage)
154 peak = minfo.PagefileUsage;
155 minfo.PeakPagefileUsage = peak;
157 minfo.TotalPhysicalMemory = total;
158 minfo.FreePhysicalMemory = total - (legacyUsed + titleUsed);
160 #else
161 return false;
162 #endif
164 return true;
167 //////////////////////////////////////////////////////////////////////////
168 void CCryMemoryManager::FakeAllocation(long size)
170 CryInterlockedExchangeAdd((LONG volatile*)&g_TotalAllocatedMemory, (LONG)size);
173 //////////////////////////////////////////////////////////////////////////
174 CCryMemoryManager::HeapHandle CCryMemoryManager::TraceDefineHeap(const char* heapName, size_t size, const void* pBase)
176 return 0;
179 //////////////////////////////////////////////////////////////////////////
180 void CCryMemoryManager::TraceHeapAlloc(HeapHandle heap, void* mem, size_t size, size_t blockSize, const char* sUsage, const char* sNameHint)
185 //////////////////////////////////////////////////////////////////////////
186 void CCryMemoryManager::TraceHeapFree(HeapHandle heap, void* mem, size_t blockSize)
191 //////////////////////////////////////////////////////////////////////////
192 void CCryMemoryManager::TraceHeapSetColor(uint32 color)
197 //////////////////////////////////////////////////////////////////////////
198 void CCryMemoryManager::TraceHeapSetLabel(const char* sLabel)
203 //////////////////////////////////////////////////////////////////////////
204 uint32 CCryMemoryManager::TraceHeapGetColor()
206 return 0;
209 //////////////////////////////////////////////////////////////////////////
210 IMemReplay* CCryMemoryManager::GetIMemReplay()
212 #if CAPTURE_REPLAY_LOG
213 return CMemReplay::GetInstance();
214 #else
215 static IMemReplay m;
216 return &m;
217 #endif
220 //////////////////////////////////////////////////////////////////////////
221 ICustomMemoryHeap* const CCryMemoryManager::CreateCustomMemoryHeapInstance(IMemoryManager::EAllocPolicy const eAllocPolicy)
223 return new CCustomMemoryHeap(eAllocPolicy);
226 IGeneralMemoryHeap* CCryMemoryManager::CreateGeneralExpandingMemoryHeap(size_t upperLimit, size_t reserveSize, const char* sUsage)
228 return new CGeneralMemoryHeap(static_cast<UINT_PTR>(0), upperLimit, reserveSize, sUsage);
231 IGeneralMemoryHeap* CCryMemoryManager::CreateGeneralMemoryHeap(void* base, size_t sz, const char* sUsage)
233 return new CGeneralMemoryHeap(base, sz, sUsage);
236 IMemoryAddressRange* CCryMemoryManager::ReserveAddressRange(size_t capacity, const char* sName)
238 return new CMemoryAddressRange(capacity, sName);
241 IPageMappingHeap* CCryMemoryManager::CreatePageMappingHeap(size_t addressSpace, const char* sName)
243 return new CPageMappingHeap(addressSpace, sName);
246 IDefragAllocator* CCryMemoryManager::CreateDefragAllocator()
248 return new CDefragAllocator();
251 void* CCryMemoryManager::AllocPages(size_t size)
253 if (size >= 256 * 1024 * 1024)
255 CryLogAlways("Virtual allocation of size %" PRISIZE_T " requested!", size);
256 gEnv->pSystem->debug_LogCallStack();
259 void* ret = NULL;
260 MEMREPLAY_SCOPE(EMemReplayAllocClass::C_UserPointer, EMemReplayUserPointerClass::C_CryMalloc);
262 #if CRY_PLATFORM_ORBIS
264 return CryModuleMemalign(size, PAGE_SIZE);
266 #elif CRY_PLATFORM_LINUX || CRY_PLATFORM_ANDROID || CRY_PLATFORM_APPLE
267 ret = mmap(0, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
268 #else
270 ret = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
272 #endif
274 if (ret)
276 MEMREPLAY_SCOPE_ALLOC(ret, size, 4096);
279 return ret;
282 void CCryMemoryManager::FreePages(void* p, size_t size)
284 UINT_PTR id = (UINT_PTR)p;
285 MEMREPLAY_SCOPE(EMemReplayAllocClass::C_UserPointer, EMemReplayUserPointerClass::C_CryMalloc);
287 #if CRY_PLATFORM_ORBIS
289 CryModuleMemalignFree(p);
291 #elif CRY_PLATFORM_LINUX || CRY_PLATFORM_ANDROID || CRY_PLATFORM_APPLE
292 #if defined(_DEBUG)
293 int ret = munmap(p, size);
294 CRY_ASSERT_MESSAGE(ret == 0, "munmap returned error.");
295 #else
296 munmap(p, size);
297 #endif
298 #else
300 VirtualFree(p, 0, MEM_RELEASE);
302 #endif
304 MEMREPLAY_SCOPE_FREE(id);
307 //////////////////////////////////////////////////////////////////////////
308 extern "C"
310 CRYMEMORYMANAGER_API void CryGetIMemoryManagerInterface(void** pIMemoryManager)
312 // Static instance of the memory manager
313 *pIMemoryManager = CCryMemoryManager::GetInstance();