!F (Profiling) (DEV-7030) Rewrite of the profiling system to have a unified interface...
[CRYENGINE.git] / Code / CryEngine / CrySystem / MemReplay.cpp
blob6b5373fbd2be468990d68e38d7d46cd13e440814
1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
3 #include "StdAfx.h"
4 #include <CryCore/BitFiddling.h>
6 #include <stdio.h>
7 #include <algorithm> // std::min()
8 #include <CrySystem/ISystem.h>
9 #include <CryCore/Platform/platform.h>
11 #include "MemoryManager.h"
13 #include "MemReplay.h"
15 #include "System.h"
16 #include <CryNetwork/CrySocks.h>
18 extern SSystemCVars g_cvars;
20 // FIXME DbgHelp broken on Durango currently
21 #if CRY_PLATFORM_WINDOWS //|| CRY_PLATFORM_DURANGO
22 #include "DebugCallStack.h"
23 #include <Psapi.h>
24 #include "DbgHelp.h"
26 namespace
28 struct CVDebugInfo
30 DWORD cvSig;
31 GUID pdbSig;
32 DWORD age;
33 char pdbName[256];
36 #endif
38 #if CRY_PLATFORM_DURANGO
39 #include <processthreadsapi.h>
40 #endif
42 #if CRY_PLATFORM_LINUX || CRY_PLATFORM_ANDROID
43 #include <sys/mman.h>
44 #if !defined(_GNU_SOURCE)
45 #define _GNU_SOURCE
46 #endif
47 #include <link.h>
48 #endif
50 #if CRY_PLATFORM_LINUX || CRY_PLATFORM_ANDROID || CRY_PLATFORM_APPLE
51 #include <sys/mman.h>
52 #endif
54 #if CRY_PLATFORM_ORBIS
55 #include <net.h>
56 #include <CryNetwork/CrySocks.h>
57 #endif
59 #if CAPTURE_REPLAY_LOG || ENABLE_STATOSCOPE
61 #pragma pack(push)
62 #pragma pack(1)
64 struct MemReplayFrameStartEvent
66 static const int EventId = MemReplayEventIds::RE_FrameStart;
68 uint32 frameId;
70 MemReplayFrameStartEvent(uint32 frameId)
71 : frameId(frameId)
73 } __PACKED;
75 struct MemReplayLabelEvent
77 static const int EventId = MemReplayEventIds::RE_Label;
79 char label[1];
81 MemReplayLabelEvent(const char* label)
83 // Assume there is room beyond this instance.
84 strcpy(this->label, label); // we're intentionally writing beyond the end of this array, so don't use cry_strcpy()
86 } __PACKED;
88 struct MemReplayAddFixedContextEvent
90 static const int EventId = MemReplayEventIds::RE_AddFixedContext;
92 uint64 threadId;
93 uint32 contextType;
94 uint32 fixedContextId;
95 char name[1];
97 MemReplayAddFixedContextEvent(uint64 threadId, const char* name, EMemStatContextType type, IMemReplay::FixedContextID ctxId)
99 this->threadId = threadId;
100 this->contextType = static_cast<uint32>(type);
101 this->fixedContextId = ctxId;
102 // We're going to assume that there actually is enough space to store the name directly in the struct.
103 strcpy(this->name, name); // we're intentionally writing beyond the end of this array, so don't use cry_strcpy()
105 } __PACKED;
107 struct MemReplayPushFixedContextEvent
109 static const int EventId = MemReplayEventIds::RE_PushFixedContext;
111 uint64 threadId;
112 IMemReplay::FixedContextID contextId;
114 MemReplayPushFixedContextEvent(uint64 threadId, IMemReplay::FixedContextID contextId)
116 this->threadId = threadId;
117 this->contextId = contextId;
119 } __PACKED;
121 struct MemReplayPushContextEvent
123 static const int EventId = MemReplayEventIds::RE_PushContext3;
125 uint64 threadId;
126 uint32 contextType;
128 // This field must be the last in the structure, and enough memory should be allocated
129 // for the structure to hold the required name.
130 char name[1];
132 MemReplayPushContextEvent(uint64 threadId, const char* name, EMemStatContextType type)
134 // We're going to assume that there actually is enough space to store the name directly in the struct.
136 this->threadId = threadId;
137 this->contextType = static_cast<uint32>(type);
138 strcpy(this->name, name); // we're intentionally writing beyond the end of this array, so don't use cry_strcpy()
140 } __PACKED;
142 struct MemReplayPopContextEvent
144 static const int EventId = MemReplayEventIds::RE_PopContext;
146 uint64 threadId;
148 explicit MemReplayPopContextEvent(uint64 threadId)
150 this->threadId = threadId;
152 } __PACKED;
154 struct MemReplayModuleRefEvent
156 static const int EventId = MemReplayEventIds::RE_ModuleRef;
158 char name[256];
159 char path[256];
160 char sig[512];
161 UINT_PTR address;
162 UINT_PTR size;
164 MemReplayModuleRefEvent(const char* name, const char* path, const UINT_PTR address, UINT_PTR size, const char* sig)
166 cry_strcpy(this->name, name);
167 cry_strcpy(this->path, path);
168 cry_strcpy(this->sig, sig);
169 this->address = address;
170 this->size = size;
172 } __PACKED;
174 struct MemReplayModuleUnRefEvent
176 static const int EventId = MemReplayEventIds::RE_ModuleUnRef;
178 UINT_PTR address;
180 MemReplayModuleUnRefEvent(UINT_PTR address)
181 : address(address) {}
182 } __PACKED;
184 struct MemReplayModuleRefShortEvent
186 static const int EventId = MemReplayEventIds::RE_ModuleShortRef;
188 char name[256];
190 MemReplayModuleRefShortEvent(const char* name)
192 cry_strcpy(this->name, name);
194 } __PACKED;
196 struct MemReplayAllocEvent
198 static const int EventId = MemReplayEventIds::RE_Alloc6;
200 uint64 threadId;
201 UINT_PTR id;
202 uint32 alignment;
203 uint32 sizeRequested;
204 uint32 sizeConsumed;
205 int32 sizeGlobal; // Inferred from changes in global memory status
207 uint16 moduleId;
208 uint16 allocClass;
209 uint16 allocSubClass;
210 uint16 callstackLength;
211 UINT_PTR callstack[1]; // Must be last.
213 MemReplayAllocEvent(uint64 threadId, uint16 moduleId, uint16 allocClass, uint16 allocSubClass, UINT_PTR id, uint32 alignment, uint32 sizeReq, uint32 sizeCon, int32 sizeGlobal)
214 : threadId(threadId)
215 , id(id)
216 , alignment(alignment)
217 , sizeRequested(sizeReq)
218 , sizeConsumed(sizeCon)
219 , sizeGlobal(sizeGlobal)
220 , moduleId(moduleId)
221 , allocClass(allocClass)
222 , allocSubClass(allocSubClass)
223 , callstackLength(0)
226 } __PACKED;
228 struct MemReplayFreeEvent
230 static const int EventId = MemReplayEventIds::RE_Free6;
232 uint64 threadId;
233 UINT_PTR id;
234 int32 sizeGlobal; // Inferred from changes in global memory status
236 uint16 moduleId;
237 uint16 allocClass;
238 uint16 allocSubClass;
240 uint16 callstackLength;
241 UINT_PTR callstack[1]; // Must be last.
243 MemReplayFreeEvent(uint64 threadId, uint16 moduleId, uint16 allocClass, uint16 allocSubClass, UINT_PTR id, int32 sizeGlobal)
244 : threadId(threadId)
245 , id(id)
246 , sizeGlobal(sizeGlobal)
247 , moduleId(moduleId)
248 , allocClass(allocClass)
249 , allocSubClass(allocSubClass)
250 , callstackLength(0)
253 } __PACKED;
255 struct MemReplayInfoEvent
257 static const int EventId = MemReplayEventIds::RE_Info3;
259 uint32 executableSize;
260 uint32 initialGlobalSize;
261 uint32 bucketsFree;
263 MemReplayInfoEvent(uint32 executableSize, uint32 initialGlobalSize, uint32 bucketsFree)
264 : executableSize(executableSize)
265 , initialGlobalSize(initialGlobalSize)
266 , bucketsFree(bucketsFree)
269 } __PACKED;
271 struct MemReplayAddressProfileEvent
273 static const int EventId = MemReplayEventIds::RE_AddressProfile2;
275 UINT_PTR rsxStart;
276 uint32 rsxLength;
278 MemReplayAddressProfileEvent(UINT_PTR rsxStart, uint32 rsxLength)
279 : rsxStart(rsxStart)
280 , rsxLength(rsxLength)
283 } __PACKED;
285 struct MemReplayAllocUsageEvent
287 static const int EventId = MemReplayEventIds::RE_AllocUsage2;
289 uint32 allocClass;
290 UINT_PTR id;
291 uint32 used;
293 MemReplayAllocUsageEvent(uint16 allocClass, UINT_PTR id, uint32 used)
294 : allocClass(allocClass)
295 , id(id)
296 , used(used)
299 } __PACKED;
301 struct MemReplayScreenshotEvent
303 static const int EventId = MemReplayEventIds::RE_Screenshot;
305 uint8 bmp[1];
307 MemReplayScreenshotEvent()
310 } __PACKED;
312 struct MemReplaySizerPushEvent
314 static const int EventId = MemReplayEventIds::RE_SizerPush;
316 char name[1];
318 MemReplaySizerPushEvent(const char* name)
320 strcpy(this->name, name);
322 } __PACKED;
324 struct MemReplaySizerPopEvent
326 static const int EventId = MemReplayEventIds::RE_SizerPop;
327 } __PACKED;
329 struct MemReplaySizerAddRangeEvent
331 static const int EventId = MemReplayEventIds::RE_SizerAddRange;
333 UINT_PTR address;
334 uint32 size;
335 int32 count;
337 MemReplaySizerAddRangeEvent(const UINT_PTR address, uint32 size, int32 count)
338 : address(address)
339 , size(size)
340 , count(count)
343 } __PACKED;
345 struct MemReplayBucketMarkEvent
347 static const int EventId = MemReplayEventIds::RE_BucketMark2;
349 UINT_PTR address;
350 uint32 length;
351 int32 index;
352 uint32 alignment;
354 MemReplayBucketMarkEvent(UINT_PTR address, uint32 length, int32 index, uint32 alignment)
355 : address(address)
356 , length(length)
357 , index(index)
358 , alignment(alignment)
361 } __PACKED;
363 struct MemReplayBucketUnMarkEvent
365 static const int EventId = MemReplayEventIds::RE_BucketUnMark2;
367 UINT_PTR address;
368 int32 index;
370 MemReplayBucketUnMarkEvent(UINT_PTR address, int32 index)
371 : address(address)
372 , index(index)
374 } __PACKED;
376 struct MemReplayAddAllocReferenceEvent
378 static const int EventId = MemReplayEventIds::RE_AddAllocReference;
380 UINT_PTR address;
381 UINT_PTR referenceId;
382 uint32 callstackLength;
383 UINT_PTR callstack[1];
385 MemReplayAddAllocReferenceEvent(UINT_PTR address, UINT_PTR referenceId)
386 : address(address)
387 , referenceId(referenceId)
388 , callstackLength(0)
391 } __PACKED;
393 struct MemReplayRemoveAllocReferenceEvent
395 static const int EventId = MemReplayEventIds::RE_RemoveAllocReference;
397 UINT_PTR referenceId;
399 MemReplayRemoveAllocReferenceEvent(UINT_PTR referenceId)
400 : referenceId(referenceId)
403 } __PACKED;
405 struct MemReplayPoolMarkEvent
407 static const int EventId = MemReplayEventIds::RE_PoolMark2;
409 UINT_PTR address;
410 uint32 length;
411 int32 index;
412 uint32 alignment;
413 char name[1];
415 MemReplayPoolMarkEvent(UINT_PTR address, uint32 length, int32 index, uint32 alignment, const char* name)
416 : address(address)
417 , length(length)
418 , index(index)
419 , alignment(alignment)
421 strcpy(this->name, name);
424 } __PACKED;
426 struct MemReplayPoolUnMarkEvent
428 static const int EventId = MemReplayEventIds::RE_PoolUnMark;
430 UINT_PTR address;
431 int32 index;
433 MemReplayPoolUnMarkEvent(UINT_PTR address, int32 index)
434 : address(address)
435 , index(index)
437 } __PACKED;
439 struct MemReplayTextureAllocContextEvent
441 static const int EventId = MemReplayEventIds::RE_TextureAllocContext2;
443 UINT_PTR address;
444 uint32 mip;
445 uint32 width;
446 uint32 height;
447 uint32 flags;
448 char name[1];
450 MemReplayTextureAllocContextEvent(UINT_PTR ptr, uint32 mip, uint32 width, uint32 height, uint32 flags, const char* name)
451 : address(ptr)
452 , mip(mip)
453 , width(width)
454 , height(height)
455 , flags(flags)
457 strcpy(this->name, name);
459 } __PACKED;
461 struct MemReplayBucketCleanupEnabledEvent
463 static const int EventId = MemReplayEventIds::RE_BucketCleanupEnabled;
465 UINT_PTR allocatorBaseAddress;
466 uint32 cleanupsEnabled;
468 MemReplayBucketCleanupEnabledEvent(UINT_PTR allocatorBaseAddress, bool cleanupsEnabled)
469 : allocatorBaseAddress(allocatorBaseAddress)
470 , cleanupsEnabled(cleanupsEnabled ? 1 : 0)
473 } __PACKED;
475 struct MemReplayReallocEvent
477 static const int EventId = MemReplayEventIds::RE_Realloc3;
479 uint64 threadId;
480 UINT_PTR oldId;
481 UINT_PTR newId;
482 uint32 alignment;
483 uint32 newSizeRequested;
484 uint32 newSizeConsumed;
485 int32 sizeGlobal; // Inferred from changes in global memory status
487 uint16 moduleId;
488 uint16 allocClass;
489 uint16 allocSubClass;
491 uint16 callstackLength;
492 UINT_PTR callstack[1]; // Must be last.
494 MemReplayReallocEvent(uint64 threadId, uint16 moduleId, uint16 allocClass, uint16 allocSubClass, UINT_PTR oldId, UINT_PTR newId, uint32 alignment, uint32 newSizeReq, uint32 newSizeCon, int32 sizeGlobal)
495 : threadId(threadId)
496 , oldId(oldId)
497 , newId(newId)
498 , alignment(alignment)
499 , newSizeRequested(newSizeReq)
500 , newSizeConsumed(newSizeCon)
501 , sizeGlobal(sizeGlobal)
502 , moduleId(moduleId)
503 , allocClass(allocClass)
504 , allocSubClass(allocSubClass)
505 , callstackLength(0)
508 } __PACKED;
510 struct MemReplayRegisterContainerEvent
512 static const int EventId = MemReplayEventIds::RE_RegisterContainer;
514 UINT_PTR key;
515 uint32 type;
517 uint32 callstackLength;
518 UINT_PTR callstack[1]; // Must be last
520 MemReplayRegisterContainerEvent(UINT_PTR key, uint32 type)
521 : key(key)
522 , type(type)
523 , callstackLength(0)
527 } __PACKED;
529 struct MemReplayUnregisterContainerEvent
531 static const int EventId = MemReplayEventIds::RE_UnregisterContainer;
533 UINT_PTR key;
535 explicit MemReplayUnregisterContainerEvent(UINT_PTR key)
536 : key(key)
540 } __PACKED;
542 struct MemReplayBindToContainerEvent
544 static const int EventId = MemReplayEventIds::RE_BindToContainer;
546 UINT_PTR key;
547 UINT_PTR ptr;
549 MemReplayBindToContainerEvent(UINT_PTR key, UINT_PTR ptr)
550 : key(key)
551 , ptr(ptr)
555 } __PACKED;
557 struct MemReplayUnbindFromContainerEvent
559 static const int EventId = MemReplayEventIds::RE_UnbindFromContainer;
561 UINT_PTR key;
562 UINT_PTR ptr;
564 MemReplayUnbindFromContainerEvent(UINT_PTR key, UINT_PTR ptr)
565 : key(key)
566 , ptr(ptr)
570 } __PACKED;
572 struct MemReplayRegisterFixedAddressRangeEvent
574 static const int EventId = MemReplayEventIds::RE_RegisterFixedAddressRange;
576 UINT_PTR address;
577 uint32 length;
579 char name[1];
581 MemReplayRegisterFixedAddressRangeEvent(UINT_PTR address, uint32 length, const char* name)
582 : address(address)
583 , length(length)
585 strcpy(this->name, name);
587 } __PACKED;
589 struct MemReplaySwapContainersEvent
591 static const int EventId = MemReplayEventIds::RE_SwapContainers;
593 UINT_PTR keyA;
594 UINT_PTR keyB;
596 MemReplaySwapContainersEvent(UINT_PTR keyA, UINT_PTR keyB)
597 : keyA(keyA)
598 , keyB(keyB)
601 } __PACKED;
603 struct MemReplayMapPageEvent
605 static const int EventId = MemReplayEventIds::RE_MapPage2;
607 UINT_PTR address;
608 uint32 length;
609 uint32 callstackLength;
610 UINT_PTR callstack[1];
612 MemReplayMapPageEvent(UINT_PTR address, uint32 length)
613 : address(address)
614 , length(length)
615 , callstackLength(0)
618 } __PACKED;
620 struct MemReplayUnMapPageEvent
622 static const int EventId = MemReplayEventIds::RE_UnMapPage;
624 UINT_PTR address;
625 uint32 length;
627 MemReplayUnMapPageEvent(UINT_PTR address, uint32 length)
628 : address(address)
629 , length(length)
632 } __PACKED;
634 #pragma pack(pop)
636 ReplayDiskWriter::ReplayDiskWriter(const char* pSuffix)
637 : m_fp(NULL)
638 , m_written(0)
640 if (pSuffix == NULL || strcmp(pSuffix, "") == 0)
642 time_t curTime;
643 time(&curTime);
644 struct tm* lt = localtime(&curTime);
646 strftime(m_filename, CRY_ARRAY_COUNT(m_filename), "memlog-%Y%m%d-%H%M%S.zmrl", lt);
648 else
650 cry_sprintf(m_filename, "memlog-%s.zmrl", pSuffix);
654 bool ReplayDiskWriter::Open()
656 #ifdef USE_FILE_HANDLE_CACHE
657 #undef fopen
658 #endif
659 char fn[MAX_PATH] = "";
660 #if CRY_PLATFORM_ORBIS
661 cry_strcpy(fn, "/data/");
662 #endif
663 cry_strcat(fn, m_filename);
664 m_fp = ::fopen(fn, "wb");
665 return m_fp != NULL;
667 #if defined(USE_FILE_HANDLE_CACHE)
668 #define fopen WrappedFopen
669 #endif
672 void ReplayDiskWriter::Close()
674 #ifdef USE_FILE_HANDLE_CACHE
675 #undef fclose
676 #endif
678 if (m_fp)
680 ::fclose(m_fp);
682 m_fp = NULL;
685 #if defined(USE_FILE_HANDLE_CACHE)
686 #define fclose WrappedFclose
687 #endif
690 int ReplayDiskWriter::Write(const void* data, size_t sz, size_t n)
692 #ifdef USE_FILE_HANDLE_CACHE
693 #undef fwrite
694 #endif
695 int res = ::fwrite(data, sz, n, m_fp);
696 #if defined(USE_FILE_HANDLE_CACHE)
697 #define fwrite WrappedFWrite
698 #endif
700 m_written += res * sz;
702 return res;
705 void ReplayDiskWriter::Flush()
707 #ifdef USE_FILE_HANDLE_CACHE
708 #undef fflush
709 #endif
711 ::fflush(m_fp);
713 #if defined(USE_FILE_HANDLE_CACHE)
714 #define fflush WrappedFflush
715 #endif
718 ReplaySocketWriter::ReplaySocketWriter(const char* address)
719 : m_port(29494)
720 , m_sock(CRY_INVALID_SOCKET)
721 , m_written(0)
723 #if defined(REPLAY_SOCKET_WRITER)
724 const char* portStart = strchr(address, ':');
725 if (portStart)
726 m_port = atoi(portStart + 1);
727 else
728 portStart = address + strlen(address);
730 cry_strcpy(m_address, address, (size_t)(portStart - address));
731 #else
732 ZeroArray(m_address);
733 #endif //#if defined(REPLAY_SOCKET_WRITER)
736 bool ReplaySocketWriter::Open()
738 #if defined(REPLAY_SOCKET_WRITER)
739 CRYINADDR_T addr32 = 0;
741 #if CRY_PLATFORM_WINAPI
742 WSADATA wsaData;
743 PREFAST_SUPPRESS_WARNING(6031); // we don't need wsaData
744 WSAStartup(MAKEWORD(2, 2), &wsaData); // ensure CrySock::socket has been initialized
745 #endif
747 addr32 = CrySock::GetAddrForHostOrIP(m_address, 0);
749 if (!addr32)
750 return false;
752 m_sock = CrySock::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
753 if (m_sock == CRY_INVALID_SOCKET)
754 return false;
756 CRYSOCKADDR_IN addr = { 0 };
757 addr.sin_family = AF_INET;
758 addr.sin_addr.s_addr = addr32;
760 int connectErr = CRY_SOCKET_ERROR;
761 for (uint16 port = m_port; (connectErr == CRY_SOCKET_ERROR) && port < (29494 + 32); ++port)
763 addr.sin_port = htons(port);
764 connectErr = CrySock::connect(m_sock, (CRYSOCKADDR*) &addr, sizeof(addr));
767 if (connectErr == CRY_SOCKET_ERROR)
769 CrySock::shutdown(m_sock, SD_BOTH);
770 CrySock::closesocket(m_sock);
772 m_sock = CRY_INVALID_SOCKET;
773 return false;
775 #endif //#if defined(REPLAY_SOCKET_WRITER)
776 return true;
779 void ReplaySocketWriter::Close()
781 #if defined(REPLAY_SOCKET_WRITER)
782 if (m_sock != CRY_INVALID_SOCKET)
784 CrySock::shutdown(m_sock, SD_BOTH);
785 CrySock::closesocket(m_sock);
786 m_sock = CRY_INVALID_SOCKET;
789 #if !CRY_PLATFORM_LINUX && !CRY_PLATFORM_ANDROID && !CRY_PLATFORM_APPLE && !CRY_PLATFORM_ORBIS
790 WSACleanup();
791 #endif
793 #endif //#if defined(REPLAY_SOCKET_WRITER)
796 int ReplaySocketWriter::Write(const void* data, size_t sz, size_t n)
798 #if defined(REPLAY_SOCKET_WRITER)
799 if (m_sock != CRY_INVALID_SOCKET)
801 const char* bdata = reinterpret_cast<const char*>(data);
802 size_t toSend = sz * n;
803 while (toSend > 0)
805 int sent = send(m_sock, bdata, toSend, 0);
807 if (sent == CRY_SOCKET_ERROR)
809 __debugbreak();
810 break;
813 toSend -= sent;
814 bdata += sent;
817 m_written += sz * n;
820 #endif //#if defined(REPLAY_SOCKET_WRITER)
822 return n;
825 void ReplaySocketWriter::Flush()
829 #endif
831 #if CAPTURE_REPLAY_LOG
833 typedef CryLockT<CRYLOCK_RECURSIVE> MemFastMutex;
834 static MemFastMutex& GetLogMutex()
836 static MemFastMutex logmutex;
837 return logmutex;
839 static uint32 g_memReplayFrameCount;
840 const int k_maxCallStackDepth = 256;
842 static volatile UINT_PTR s_replayLastGlobal = 0;
843 static volatile int s_replayStartingFree = 0;
845 static volatile threadID s_recordThreadId = threadID(-1);
847 inline bool ThreadIsNotFlushing()
849 return s_recordThreadId != CryGetCurrentThreadId();
852 // No write thread: While writing out data we need to avoid recording new entries, as we are under lock
853 // With write Thread: Just ignore allocations from the recording thread
854 struct RecordingThreadScope
856 RecordingThreadScope() { assert(s_recordThreadId == threadID(-1)); s_recordThreadId = CryGetCurrentThreadId(); }
857 ~RecordingThreadScope() { assert(s_recordThreadId != threadID(-1)); s_recordThreadId = threadID(-1); }
860 #if CRY_PLATFORM_WINDOWS || CRY_PLATFORM_DURANGO
862 void* ReplayAllocatorBase::ReserveAddressSpace(size_t sz)
864 return VirtualAlloc(NULL, sz, MEM_RESERVE, PAGE_READWRITE);
867 void ReplayAllocatorBase::UnreserveAddressSpace(void* base, void* committedEnd)
869 VirtualFree(base, 0, MEM_RELEASE);
872 void* ReplayAllocatorBase::MapAddressSpace(void* addr, size_t sz)
874 return VirtualAlloc(addr, sz, MEM_COMMIT, PAGE_READWRITE);
877 #elif CRY_PLATFORM_ORBIS
879 void* ReplayAllocatorBase::ReserveAddressSpace(size_t sz)
881 return VirtualAllocator::AllocateVirtualAddressSpace(sz);
884 void ReplayAllocatorBase::UnreserveAddressSpace(void* base, void* committedEnd)
886 VirtualAllocator::FreeVirtualAddressSpace(base);
889 void* ReplayAllocatorBase::MapAddressSpace(void* addr, size_t sz)
891 for (size_t i = 0; i < sz; i += PAGE_SIZE)
893 if (!VirtualAllocator::MapPage((char*)addr + i))
895 for (size_t k = 0; k < i; k += PAGE_SIZE)
897 VirtualAllocator::UnmapPage((char*)addr + k);
899 return NULL;
902 return addr;
905 #elif CRY_PLATFORM_LINUX || CRY_PLATFORM_ANDROID || CRY_PLATFORM_APPLE
906 void* ReplayAllocatorBase::ReserveAddressSpace(size_t sz)
908 return mmap(NULL, sz, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
911 void ReplayAllocatorBase::UnreserveAddressSpace(void* base, void* committedEnd)
913 int res = munmap(base, reinterpret_cast<char*>(committedEnd) - reinterpret_cast<char*>(base));
914 (void) res;
915 assert(res == 0);
918 void* ReplayAllocatorBase::MapAddressSpace(void* addr, size_t sz)
920 int res = mprotect(addr, sz, PROT_READ | PROT_WRITE);
921 return addr;
923 #elif CRY_PLATFORM_LINUX || CRY_PLATFORM_ANDROID
925 void* ReplayAllocatorBase::ReserveAddressSpace(size_t sz)
927 return mmap(NULL, sz, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
930 void ReplayAllocatorBase::UnreserveAddressSpace(void* base, void* committedEnd)
932 int res = munmap(base, reinterpret_cast<char*>(committedEnd) - reinterpret_cast<char*>(base));
935 void* ReplayAllocatorBase::MapAddressSpace(void* addr, size_t sz)
937 int res = mprotect(addr, sz, PROT_READ | PROT_WRITE);
938 return addr;
941 #elif CRY_PLATFORM_ORBIS
943 void* ReplayAllocatorBase::ReserveAddressSpace(size_t sz)
945 return VirtualAllocator::AllocateVirtualAddressSpace(sz);
948 void ReplayAllocatorBase::UnreserveAddressSpace(void* base, void* committedEnd)
950 VirtualAllocator::FreeVirtualAddressSpace(base);
953 void* ReplayAllocatorBase::MapAddressSpace(void* addr, size_t sz)
955 for (size_t i = 0; i < sz; i += PAGE_SIZE)
957 if (!VirtualAllocator::MapPage((char*)addr + i))
959 for (size_t k = 0; k < i; k += PAGE_SIZE)
961 VirtualAllocator::UnmapPage((char*)addr + k);
963 return NULL;
966 return addr;
969 #endif
971 namespace
973 //helper functions to become strict aliasing warning conform
974 ILINE void** CastCallstack(uint32* pCallstack)
976 return alias_cast<void**>(pCallstack);
978 ILINE void** CastCallstack(uint64* pCallstack)
980 return alias_cast<void**>(pCallstack);
982 #if !CRY_PLATFORM_WINDOWS && !CRY_PLATFORM_DURANGO && !CRY_PLATFORM_LINUX && !CRY_PLATFORM_ANDROID
983 ILINE void** CastCallstack(UINT_PTR* pCallstack)
985 return alias_cast<void**>(pCallstack);
987 #endif
988 ILINE volatile long* LongPtr(volatile int* pI)
990 return alias_cast<volatile long*>(pI);
993 void RetrieveMemReplaySuffix(char* pSuffixBuffer, const char* lwrCommandLine, int bufferLength)
995 cry_strcpy(pSuffixBuffer, bufferLength, "");
996 const char* pSuffix = strstr(lwrCommandLine, "-memreplaysuffix");
997 if (pSuffix != NULL)
999 pSuffix += strlen("-memreplaysuffix");
1000 while (1)
1002 if (*pSuffix == '\0' || *pSuffix == '+' || *pSuffix == '-')
1004 pSuffix = NULL;
1005 break;
1007 if (*pSuffix != ' ')
1008 break;
1009 ++pSuffix;
1011 if (pSuffix != NULL)
1013 const char* pEnd = pSuffix;
1014 while (*pEnd != '\0' && *pEnd != ' ')
1015 ++pEnd;
1016 cry_strcpy(pSuffixBuffer, bufferLength, pSuffix, (size_t)(pEnd - pSuffix));
1022 ReplayAllocator::ReplayAllocator()
1024 m_heap = ReserveAddressSpace(MaxSize);
1025 if (!m_heap)
1026 __debugbreak();
1028 m_commitEnd = m_heap;
1029 m_allocEnd = m_heap;
1032 ReplayAllocator::~ReplayAllocator()
1034 Free();
1037 void ReplayAllocator::Reserve(size_t sz)
1039 CryAutoLock<CryCriticalSectionNonRecursive> lock(m_lock);
1041 INT_PTR commitEnd = reinterpret_cast<INT_PTR>(m_commitEnd);
1042 INT_PTR newCommitEnd = std::min<INT_PTR>(commitEnd + sz, commitEnd + MaxSize);
1044 if ((newCommitEnd - commitEnd) > 0)
1046 LPVOID res = MapAddressSpace(m_commitEnd, newCommitEnd - commitEnd);
1047 if (!res)
1048 __debugbreak();
1051 m_commitEnd = reinterpret_cast<void*>(newCommitEnd);
1054 void* ReplayAllocator::Allocate(size_t sz)
1056 CryAutoLock<CryCriticalSectionNonRecursive> lock(m_lock);
1058 sz = (sz + 7) & ~7;
1060 uint8* alloc = reinterpret_cast<uint8*>(m_allocEnd);
1061 uint8* newEnd = reinterpret_cast<uint8*>(m_allocEnd) + sz;
1063 if (newEnd > reinterpret_cast<uint8*>(m_commitEnd))
1065 uint8* alignedEnd = reinterpret_cast<uint8*>((reinterpret_cast<INT_PTR>(newEnd) + (PageSize - 1)) & ~(PageSize - 1));
1067 if (alignedEnd > reinterpret_cast<uint8*>(m_allocEnd) + MaxSize)
1068 __debugbreak();
1070 LPVOID res = MapAddressSpace(m_commitEnd, alignedEnd - reinterpret_cast<uint8*>(m_commitEnd));
1071 if (!res)
1072 __debugbreak();
1074 m_commitEnd = alignedEnd;
1077 m_allocEnd = newEnd;
1079 return alloc;
1082 void ReplayAllocator::Free()
1084 CryAutoLock<CryCriticalSectionNonRecursive> lock(m_lock);
1086 UnreserveAddressSpace(m_heap, m_commitEnd);
1089 ReplayCompressor::ReplayCompressor(ReplayAllocator& allocator, IReplayWriter* writer)
1090 : m_allocator(&allocator)
1091 , m_writer(writer)
1093 m_zSize = 0;
1095 memset(&m_compressStream, 0, sizeof(m_compressStream));
1096 m_compressStream.zalloc = &ReplayCompressor::zAlloc;
1097 m_compressStream.zfree = &ReplayCompressor::zFree;
1098 m_compressStream.opaque = this;
1099 deflateInit(&m_compressStream, 2);//Z_DEFAULT_COMPRESSION);
1101 uint64 streamLen = 0;
1102 m_writer->Write(&streamLen, sizeof(streamLen), 1);
1104 m_compressTarget = (uint8*) m_allocator->Allocate(CompressTargetSize);
1107 ReplayCompressor::~ReplayCompressor()
1109 deflateEnd(&m_compressStream);
1110 m_compressTarget = NULL;
1111 m_writer = NULL;
1114 void ReplayCompressor::Flush()
1116 m_writer->Flush();
1119 void ReplayCompressor::Write(const uint8* data, size_t len)
1121 if (CompressTargetSize < len)
1122 __debugbreak();
1124 m_compressStream.next_in = const_cast<uint8*>(data);
1125 m_compressStream.avail_in = len;
1126 m_compressStream.next_out = m_compressTarget;
1127 m_compressStream.avail_out = CompressTargetSize;
1128 m_compressStream.total_out = 0;
1132 int err = deflate(&m_compressStream, Z_SYNC_FLUSH);
1134 if ((err == Z_OK && m_compressStream.avail_out == 0) || (err == Z_BUF_ERROR && m_compressStream.avail_out == 0))
1136 int total_out = m_compressStream.total_out;
1137 m_writer->Write(m_compressTarget, total_out * sizeof(uint8), 1);
1139 m_compressStream.next_out = m_compressTarget;
1140 m_compressStream.avail_out = CompressTargetSize;
1141 m_compressStream.total_out = 0;
1143 continue;
1145 else if (m_compressStream.avail_in == 0)
1147 int total_out = m_compressStream.total_out;
1148 m_writer->Write(m_compressTarget, total_out * sizeof(uint8), 1);
1150 else
1152 // Shrug?
1155 break;
1157 while (true);
1159 Flush();
1162 voidpf ReplayCompressor::zAlloc(voidpf opaque, uInt items, uInt size)
1164 ReplayCompressor* str = reinterpret_cast<ReplayCompressor*>(opaque);
1165 return str->m_allocator->Allocate(items * size);
1168 void ReplayCompressor::zFree(voidpf opaque, voidpf address)
1170 // Doesn't seem to be called, except when shutting down and then we'll just free it all anyway
1173 #if REPLAY_RECORD_THREADED
1175 ReplayRecordThread::ReplayRecordThread(ReplayCompressor* compressor)
1176 : m_compressor(compressor)
1178 m_nextCommand = CMD_Idle;
1179 m_nextData = NULL;
1180 m_nextLength = 0;
1183 void ReplayRecordThread::Flush()
1185 m_mtx.Lock();
1187 while (m_nextCommand != CMD_Idle)
1189 m_mtx.Unlock();
1190 CrySleep(1);
1191 m_mtx.Lock();
1194 m_nextCommand = CMD_Flush;
1195 m_mtx.Unlock();
1196 m_cond.NotifySingle();
1199 void ReplayRecordThread::Write(const uint8* data, size_t len)
1201 m_mtx.Lock();
1203 while (m_nextCommand != CMD_Idle)
1205 m_mtx.Unlock();
1206 CrySleep(1);
1207 m_mtx.Lock();
1210 m_nextData = data;
1211 m_nextLength = len;
1212 m_nextCommand = CMD_Write;
1213 m_mtx.Unlock();
1214 m_cond.NotifySingle();
1217 void ReplayRecordThread::ThreadEntry()
1219 RecordingThreadScope recordScope;
1221 while (true)
1223 m_mtx.Lock();
1225 while (m_nextCommand == CMD_Idle)
1226 m_cond.Wait(m_mtx);
1228 switch (m_nextCommand)
1230 case CMD_Stop:
1231 m_mtx.Unlock();
1232 m_nextCommand = CMD_Idle;
1233 return;
1235 case CMD_Write:
1237 size_t length = m_nextLength;
1238 const uint8* data = m_nextData;
1240 m_nextLength = 0;
1241 m_nextData = NULL;
1243 m_compressor->Write(data, length);
1245 break;
1247 case CMD_Flush:
1248 m_compressor->Flush();
1249 break;
1252 m_nextCommand = CMD_Idle;
1254 m_mtx.Unlock();
1258 void ReplayRecordThread::SignalStopWork()
1260 m_mtx.Lock();
1262 while (m_nextCommand != CMD_Idle)
1264 m_mtx.Unlock();
1265 CrySleep(1);
1266 m_mtx.Lock();
1269 m_nextCommand = CMD_Stop;
1270 m_cond.NotifySingle();
1272 while (m_nextCommand != CMD_Idle)
1274 m_mtx.Unlock();
1275 CrySleep(1);
1276 m_mtx.Lock();
1279 m_mtx.Unlock();
1282 #endif
1284 ReplayLogStream::ReplayLogStream()
1285 : m_isOpen(0)
1286 , m_buffer(nullptr)
1287 , m_bufferSize(0)
1288 , m_bufferEnd(nullptr)
1289 , m_uncompressedLen(0)
1290 , m_sequenceCheck(0)
1291 , m_bufferA(nullptr)
1292 , m_compressor(nullptr)
1293 #if REPLAY_RECORD_THREADED
1294 , m_bufferB(nullptr)
1295 , m_recordThread(nullptr)
1296 #endif
1297 , m_writer(nullptr)
1301 ReplayLogStream::~ReplayLogStream()
1303 if (IsOpen())
1305 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
1306 Close();
1310 bool ReplayLogStream::Open(const char* openString)
1312 using std::swap;
1314 m_allocator.Reserve(512 * 1024);
1316 while (isspace((unsigned char) *openString))
1317 ++openString;
1319 const char* sep = strchr(openString, ':');
1320 if (!sep)
1321 sep = openString + strlen(openString);
1323 char destination[32];
1324 const char* arg = "";
1326 if (sep != openString)
1328 while (isspace((unsigned char) sep[-1]))
1329 --sep;
1331 cry_strcpy(destination, openString, (size_t)(sep - openString));
1333 arg = sep;
1335 if (*arg)
1337 ++arg;
1338 while (isspace((unsigned char) *arg))
1339 ++arg;
1342 else
1344 cry_strcpy(destination, "disk");
1347 IReplayWriter* writer = NULL;
1349 if (strcmp(destination, "disk") == 0)
1351 writer = new(m_allocator.Allocate(sizeof(ReplayDiskWriter)))ReplayDiskWriter(arg);
1353 else if (strcmp(destination, "socket") == 0)
1355 writer = new(m_allocator.Allocate(sizeof(ReplaySocketWriter)))ReplaySocketWriter(arg);
1358 if (writer && writer->Open() == false)
1360 fprintf(stdout, "Failed to open writer\n");
1361 writer->~IReplayWriter();
1362 writer = NULL;
1364 m_allocator.Free();
1365 return false;
1368 swap(m_writer, writer);
1370 m_bufferSize = 64 * 1024;
1371 m_bufferA = (uint8*) m_allocator.Allocate(m_bufferSize);
1373 #if REPLAY_RECORD_THREADED
1374 m_bufferB = (uint8*) m_allocator.Allocate(m_bufferSize);
1375 #endif
1377 m_buffer = m_bufferA;
1378 m_compressor = new(m_allocator.Allocate(sizeof(ReplayCompressor)))ReplayCompressor(m_allocator, m_writer);
1381 m_bufferEnd = &m_buffer[0];
1383 // Write stream header.
1384 const uint8 version = 5;
1385 uint32 platform = MemReplayPlatformIds::PI_Unknown;
1387 #if CRY_PLATFORM_ORBIS
1388 platform = MemReplayPlatformIds::PI_Orbis;
1389 #elif CRY_PLATFORM_DURANGO
1390 platform = MemReplayPlatformIds::PI_Durango;
1391 #elif CRY_PLATFORM_WINDOWS || CRY_PLATFORM_LINUX || CRY_PLATFORM_ANDROID || CRY_PLATFORM_APPLE
1392 platform = MemReplayPlatformIds::PI_PC;
1393 #endif
1395 MemReplayLogHeader hdr(('M' << 0) | ('R' << 8) | ('L' << 16) | (version << 24), platform, sizeof(void*) * 8);
1396 memcpy(&m_buffer[0], &hdr, sizeof(hdr));
1397 m_bufferEnd += sizeof(hdr);
1399 CryInterlockedIncrement(&m_isOpen);
1402 return true;
1405 void ReplayLogStream::Close()
1407 using std::swap;
1409 // Flag it as closed here, to prevent recursion into the memory logging as it is closing.
1410 CryInterlockedDecrement(&m_isOpen);
1412 Flush();
1413 Flush();
1414 Flush();
1416 #if REPLAY_RECORD_THREADED
1417 if (m_recordThread)
1419 m_recordThread->SignalStopWork();
1420 gEnv->pThreadManager->JoinThread(m_recordThread, eJM_Join);
1421 m_recordThread->~ReplayRecordThread();
1422 m_recordThread = NULL;
1424 #endif
1426 if (m_compressor)
1428 m_compressor->~ReplayCompressor();
1429 m_compressor = NULL;
1432 m_writer->Close();
1433 m_writer = NULL;
1435 m_buffer = NULL;
1436 m_bufferEnd = 0;
1437 m_bufferSize = 0;
1439 m_allocator.Free();
1442 bool ReplayLogStream::EnableAsynchMode()
1444 #if REPLAY_RECORD_THREADED
1445 // System to create thread is not running yet
1446 if (!gEnv || !gEnv->pSystem)
1447 return false;
1449 // Asynch mode already enabled
1450 if (m_recordThread)
1451 return true;
1453 // Write out pending data
1454 Flush();
1456 // Try create ReplayRecord thread
1457 m_recordThread = new(m_allocator.Allocate(sizeof(ReplayRecordThread)))ReplayRecordThread(m_compressor);
1458 if (!gEnv->pThreadManager->SpawnThread(m_recordThread, "ReplayRecord"))
1460 CRY_ASSERT_MESSAGE(false, "Error spawning \"ReplayRecord\" thread.");
1461 m_recordThread->~ReplayRecordThread();
1462 m_recordThread = NULL;
1463 return false;
1466 return true;
1467 #else
1468 return false;
1469 #endif
1472 void ReplayLogStream::Flush()
1474 if (m_writer != NULL)
1476 m_uncompressedLen += m_bufferEnd - &m_buffer[0];
1478 #if REPLAY_RECORD_THREADED
1479 if (m_recordThread)
1481 m_recordThread->Write(&m_buffer[0], m_bufferEnd - &m_buffer[0]);
1482 m_buffer = (m_buffer == m_bufferA) ? m_bufferB : m_bufferA;
1484 else
1485 #endif
1487 RecordingThreadScope writeScope;
1488 m_compressor->Write(&m_buffer[0], m_bufferEnd - &m_buffer[0]);
1492 m_bufferEnd = &m_buffer[0];
1495 void ReplayLogStream::FullFlush()
1497 #if REPLAY_RECORD_THREADED
1498 if (m_recordThread)
1500 m_recordThread->Flush();
1502 else
1503 #endif
1505 RecordingThreadScope writeScope;
1506 m_compressor->Flush();
1510 size_t ReplayLogStream::GetSize() const
1512 return m_allocator.GetCommittedSize();
1515 uint64 ReplayLogStream::GetWrittenLength() const
1517 return m_writer ? m_writer->GetWrittenLength() : 0ULL;
1520 uint64 ReplayLogStream::GetUncompressedLength() const
1522 return m_uncompressedLen;
1525 #define SIZEOF_MEMBER(cls, mbr) (sizeof(reinterpret_cast<cls*>(0)->mbr))
1527 static bool g_memReplayPaused = false;
1529 //////////////////////////////////////////////////////////////////////////
1530 // CMemReplay class implementation.
1531 //////////////////////////////////////////////////////////////////////////
1533 //////////////////////////////////////////////////////////////////////////
1535 static int GetCurrentSysAlloced()
1537 int trackingUsage = 0;//m_stream.GetSize();
1538 IMemoryManager::SProcessMemInfo mi;
1539 CCryMemoryManager::GetInstance()->GetProcessMemInfo(mi);
1540 return (uint32)(mi.PagefileUsage - trackingUsage);
1543 #if !CRY_PLATFORM_ORBIS
1544 int CMemReplay::GetCurrentExecutableSize()
1546 return GetCurrentSysAlloced();
1548 #endif
1550 //////////////////////////////////////////////////////////////////////////
1551 static UINT_PTR GetCurrentSysFree()
1553 IMemoryManager::SProcessMemInfo mi;
1554 CCryMemoryManager::GetInstance()->GetProcessMemInfo(mi);
1555 return (UINT_PTR)-(INT_PTR)mi.PagefileUsage;
1558 void CReplayModules::RefreshModules(FModuleLoad onLoad, FModuleUnload onUnload, void* pUser)
1560 #if CRY_PLATFORM_WINDOWS
1561 HMODULE hMods[1024];
1562 HANDLE hProcess;
1563 DWORD cbNeeded;
1564 unsigned int i;
1566 // Get a list of all the modules in this process.
1567 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, GetCurrentProcessId());
1568 if (NULL != hProcess)
1570 if (SymInitialize(hProcess, NULL, TRUE))
1572 decltype(&EnumProcessModules) pfEnumProcessModules;
1573 decltype(&GetModuleFileNameEx) pfGetModuleFileNameEx;
1574 decltype(&GetModuleInformation) pfGetModuleInformation;
1576 HMODULE hPsapiModule = ::LoadLibraryA("psapi.dll");
1577 if (hPsapiModule)
1579 pfEnumProcessModules = (decltype(pfEnumProcessModules)) GetProcAddress(hPsapiModule, "EnumProcessModules");
1580 pfGetModuleFileNameEx = (decltype(pfGetModuleFileNameEx)) GetProcAddress(hPsapiModule, "GetModuleFileNameEx");
1581 pfGetModuleInformation = (decltype(pfGetModuleInformation)) GetProcAddress(hPsapiModule, "GetModuleInformation");
1584 if(pfEnumProcessModules && pfGetModuleFileNameEx && pfGetModuleInformation)
1586 if (pfEnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
1588 for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
1590 TCHAR szModName[MAX_PATH];
1591 // Get the full path to the module's file.
1592 if (pfGetModuleFileNameEx(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(TCHAR)))
1594 char pdbPath[1024];
1595 cry_strcpy(pdbPath, szModName);
1596 char* pPdbPathExt = strrchr(pdbPath, '.');
1597 if (pPdbPathExt)
1598 strcpy(pPdbPathExt, ".pdb");
1599 else
1600 cry_strcat(pdbPath, ".pdb");
1602 MODULEINFO modInfo;
1603 pfGetModuleInformation(hProcess, hMods[i], &modInfo, sizeof(modInfo));
1605 IMAGEHLP_MODULE64 ihModInfo;
1606 memset(&ihModInfo, 0, sizeof(ihModInfo));
1607 ihModInfo.SizeOfStruct = sizeof(ihModInfo);
1608 if (SymGetModuleInfo64(hProcess, (DWORD64)modInfo.lpBaseOfDll, &ihModInfo))
1610 cry_strcpy(pdbPath, ihModInfo.CVData);
1612 if (ihModInfo.PdbSig70.Data1 == 0)
1614 DWORD debugEntrySize;
1615 IMAGE_DEBUG_DIRECTORY* pDebugDirectory = (IMAGE_DEBUG_DIRECTORY*)ImageDirectoryEntryToDataEx(modInfo.lpBaseOfDll, TRUE, IMAGE_DIRECTORY_ENTRY_DEBUG, &debugEntrySize, NULL);
1616 if (pDebugDirectory)
1618 size_t numDirectories = debugEntrySize / sizeof(IMAGE_DEBUG_DIRECTORY);
1619 for (size_t dirIdx = 0; dirIdx != numDirectories; ++dirIdx)
1621 IMAGE_DEBUG_DIRECTORY& dir = pDebugDirectory[dirIdx];
1622 if (dir.Type == 2)
1624 CVDebugInfo* pCVInfo = (CVDebugInfo*)((char*)modInfo.lpBaseOfDll + dir.AddressOfRawData);
1625 ihModInfo.PdbSig70 = pCVInfo->pdbSig;
1626 ihModInfo.PdbAge = pCVInfo->age;
1627 cry_strcpy(pdbPath, pCVInfo->pdbName);
1628 break;
1634 ModuleLoadDesc mld;
1635 cry_sprintf(mld.sig, "%p, %08X, %08X, {%08X-%04X-%04X-%02X %02X-%02X %02X %02X %02X %02X %02X}, %d\n",
1636 modInfo.lpBaseOfDll, modInfo.SizeOfImage, ihModInfo.TimeDateStamp,
1637 ihModInfo.PdbSig70.Data1, ihModInfo.PdbSig70.Data2, ihModInfo.PdbSig70.Data3,
1638 ihModInfo.PdbSig70.Data4[0], ihModInfo.PdbSig70.Data4[1],
1639 ihModInfo.PdbSig70.Data4[2], ihModInfo.PdbSig70.Data4[3],
1640 ihModInfo.PdbSig70.Data4[4], ihModInfo.PdbSig70.Data4[5],
1641 ihModInfo.PdbSig70.Data4[6], ihModInfo.PdbSig70.Data4[7],
1642 ihModInfo.PdbAge);
1644 cry_strcpy(mld.name, szModName);
1645 cry_strcpy(mld.path, pdbPath);
1646 mld.address = (UINT_PTR)modInfo.lpBaseOfDll;
1647 mld.size = modInfo.SizeOfImage;
1648 onLoad(pUser, mld);
1654 SymCleanup(hProcess);
1657 CloseHandle(hProcess);
1659 #elif CRY_PLATFORM_DURANGO && 0
1661 // Get a list of all the modules in this process.
1662 EnumModuleState ems;
1663 ems.hProcess = GetCurrentProcess();
1664 ems.pSelf = this;
1665 ems.onLoad = onLoad;
1666 ems.pOnLoadUser = pUser;
1667 if (NULL != ems.hProcess)
1669 if (SymInitialize(ems.hProcess, NULL, TRUE))
1671 EnumerateLoadedModulesEx(ems.hProcess, EnumModules_Durango, &ems);
1673 SymCleanup(ems.hProcess);
1676 #elif CRY_PLATFORM_LINUX || CRY_PLATFORM_ANDROID
1677 EnumModuleState ems;
1678 ems.hProcess = 0;
1679 ems.pSelf = this;
1680 ems.onLoad = onLoad;
1681 ems.pOnLoadUser = pUser;
1682 dl_iterate_phdr(EnumModules_Linux, &ems);
1683 #elif CRY_PLATFORM_ORBIS
1684 RefreshModules_Orbis(onLoad, pUser);
1685 #endif
1688 #if CRY_PLATFORM_DURANGO && 0
1690 BOOL CReplayModules::EnumModules_Durango(PCSTR moduleName, DWORD64 moduleBase, ULONG moduleSize, PVOID userContext)
1692 EnumModuleState& ems = *static_cast<EnumModuleState*>(userContext);
1694 char pdbPath[1024];
1695 cry_strcpy(pdbPath, moduleName);
1696 char* pPdbPathExt = strrchr(pdbPath, '.');
1697 if (pPdbPathExt)
1698 strcpy(pPdbPathExt, ".pdb");
1699 else
1700 cry_strcat(pdbPath, ".pdb");
1702 IMAGEHLP_MODULE64 ihModInfo;
1703 memset(&ihModInfo, 0, sizeof(ihModInfo));
1704 ihModInfo.SizeOfStruct = sizeof(ihModInfo);
1705 if (SymGetModuleInfo64(ems.hProcess, moduleBase, &ihModInfo))
1707 cry_strcpy(pdbPath, ihModInfo.CVData);
1709 if (ihModInfo.PdbSig70.Data1 == 0)
1711 DWORD debugEntrySize;
1712 IMAGE_DEBUG_DIRECTORY* pDebugDirectory = (IMAGE_DEBUG_DIRECTORY*)ImageDirectoryEntryToDataEx((PVOID)moduleBase, TRUE, IMAGE_DIRECTORY_ENTRY_DEBUG, &debugEntrySize, NULL);
1713 if (pDebugDirectory)
1715 size_t numDirectories = debugEntrySize / sizeof(IMAGE_DEBUG_DIRECTORY);
1716 for (size_t dirIdx = 0; dirIdx != numDirectories; ++dirIdx)
1718 IMAGE_DEBUG_DIRECTORY& dir = pDebugDirectory[dirIdx];
1719 if (dir.Type == 2)
1721 CVDebugInfo* pCVInfo = (CVDebugInfo*)((char*)moduleBase + dir.AddressOfRawData);
1722 ihModInfo.PdbSig70 = pCVInfo->pdbSig;
1723 ihModInfo.PdbAge = pCVInfo->age;
1724 cry_strcpy(pdbPath, pCVInfo->pdbName);
1725 break;
1731 ModuleLoadDesc mld;
1732 cry_sprintf(mld.sig, "%p, %08X, %08X, {%08X-%04X-%04X-%02X %02X-%02X %02X %02X %02X %02X %02X}, %d\n",
1733 moduleBase, moduleSize, ihModInfo.TimeDateStamp,
1734 ihModInfo.PdbSig70.Data1, ihModInfo.PdbSig70.Data2, ihModInfo.PdbSig70.Data3,
1735 ihModInfo.PdbSig70.Data4[0], ihModInfo.PdbSig70.Data4[1],
1736 ihModInfo.PdbSig70.Data4[2], ihModInfo.PdbSig70.Data4[3],
1737 ihModInfo.PdbSig70.Data4[4], ihModInfo.PdbSig70.Data4[5],
1738 ihModInfo.PdbSig70.Data4[6], ihModInfo.PdbSig70.Data4[7],
1739 ihModInfo.PdbAge);
1741 cry_strcpy(mld.name, moduleName);
1742 cry_strcpy(mld.path, pdbPath);
1743 mld.address = moduleBase;
1744 mld.size = moduleSize;
1745 ems.onLoad(ems.pOnLoadUser, mld);
1748 return TRUE;
1751 #endif
1753 #if CRY_PLATFORM_LINUX || CRY_PLATFORM_ANDROID
1754 int CReplayModules::EnumModules_Linux(struct dl_phdr_info* info, size_t size, void* userContext)
1756 EnumModuleState& ems = *reinterpret_cast<EnumModuleState*>(userContext);
1758 ModuleLoadDesc mld;
1759 mld.address = info->dlpi_addr;
1760 cry_strcpy(mld.name, info->dlpi_name);
1761 cry_strcpy(mld.path, info->dlpi_name);
1763 Module& module = ems.pSelf->m_knownModules[ems.pSelf->m_numKnownModules];
1764 ems.pSelf->m_numKnownModules++;
1765 module.baseAddress = info->dlpi_addr;
1766 module.timestamp = 0;
1768 uint64 memSize = 0;
1769 for (int j = 0; j < info->dlpi_phnum; j++)
1771 memSize += info->dlpi_phdr[j].p_memsz;
1773 mld.size = (UINT_PTR)memSize;
1774 module.size = (UINT_PTR)memSize;
1776 ems.onLoad(ems.pOnLoadUser, mld);
1778 return 0;
1780 #endif
1782 // Ensure IMemReplay is not destroyed during atExit() procedure which has no defined destruction order.
1783 // In some cases it was destroyed while still being used.
1784 CRY_ALIGN(8) char g_memReplay[sizeof(CMemReplay)];
1785 CMemReplay* CMemReplay::GetInstance()
1787 static bool bIsInit = false;
1788 if (!bIsInit)
1790 new(&g_memReplay)CMemReplay();
1791 bIsInit = true;
1794 return alias_cast<CMemReplay*>(g_memReplay);
1797 CMemReplay::CMemReplay()
1798 : m_allocReference(0)
1799 , m_scopeDepth(0)
1800 , m_scopeClass(EMemReplayAllocClass::UserPointer)
1801 , m_scopeSubClass(EMemReplayUserPointerClass::Unknown)
1802 , m_scopeModuleId(0)
1806 CMemReplay::~CMemReplay()
1810 //////////////////////////////////////////////////////////////////////////
1811 void CMemReplay::DumpStats()
1813 CMemReplay::DumpSymbols();
1816 //////////////////////////////////////////////////////////////////////////
1817 void CMemReplay::DumpSymbols()
1819 if (m_stream.IsOpen())
1821 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
1822 if (m_stream.IsOpen())
1824 RecordModules();
1826 m_stream.FullFlush();
1831 //////////////////////////////////////////////////////////////////////////
1832 void CMemReplay::StartOnCommandLine(const char* cmdLine)
1834 const char* mrCmd = 0;
1836 CryStackStringT<char, 2048> lwrCmdLine = cmdLine;
1837 lwrCmdLine.MakeLower();
1839 if ((mrCmd = strstr(lwrCmdLine.c_str(), "-memreplay")) != nullptr)
1841 bool bPaused = strstr(lwrCmdLine.c_str(), "-memreplaypaused") != nullptr;
1843 //Retrieve file suffix if present
1844 const int bufferLength = 64;
1845 char openCmd[bufferLength] = "disk:";
1847 // Try and detect a new style open string, and fall back to the old suffix format if it fails.
1848 if (
1849 (strncmp(mrCmd, "-memreplay disk", 15) == 0) ||
1850 (strncmp(mrCmd, "-memreplay socket", 17) == 0))
1852 const char* arg = mrCmd + strlen("-memreplay ");
1853 const char* argEnd = arg;
1854 while (*argEnd && !isspace((unsigned char) *argEnd))
1855 ++argEnd;
1856 cry_strcpy(openCmd, arg, (size_t)(argEnd - arg));
1858 else
1860 RetrieveMemReplaySuffix(openCmd + 5, lwrCmdLine.c_str(), bufferLength - 5);
1863 Start(bPaused, openCmd);
1867 //////////////////////////////////////////////////////////////////////////
1868 void CMemReplay::Start(bool bPaused, const char* openString)
1870 if (!openString)
1872 openString = "disk";
1875 if(m_stream.IsOpen())
1877 const char* label = nullptr;
1878 if (bPaused && !g_memReplayPaused)
1879 label = "Pausing MemReplay";
1880 else if (!bPaused && g_memReplayPaused)
1881 label = "Resuming MemReplay";
1883 if(label)
1884 new(m_stream.AllocateRawEvent<MemReplayLabelEvent>(strlen(label)))MemReplayLabelEvent(label);
1887 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
1888 g_memReplayPaused = bPaused;
1890 if (!m_stream.IsOpen())
1892 if (m_stream.Open(openString))
1894 s_replayLastGlobal = GetCurrentSysFree();
1895 int initialGlobal = GetCurrentSysAlloced();
1896 int exeSize = GetCurrentExecutableSize();
1898 #if CRY_PLATFORM_ORBIS
1899 // Also record used space in the system allocator
1900 exeSize += CrySystemCrtGetUsedSpace();
1901 #endif
1903 m_stream.WriteEvent(MemReplayAddressProfileEvent(0xffffffff, 0));
1904 m_stream.WriteEvent(MemReplayInfoEvent(exeSize, initialGlobal, s_replayStartingFree));
1905 m_stream.WriteEvent(MemReplayFrameStartEvent(g_memReplayFrameCount++));
1907 #if CRY_PLATFORM_DURANGO
1908 m_stream.WriteEvent(MemReplayModuleRefShortEvent("minidump"));
1909 #endif
1911 #if CRY_PLATFORM_ORBIS
1912 m_stream.WriteEvent(MemReplayModuleRefShortEvent("orbis"));
1913 #endif
1918 //////////////////////////////////////////////////////////////////////////
1919 void CMemReplay::Stop()
1921 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
1922 if (m_stream.IsOpen())
1924 g_memReplayPaused = false; // unpause or modules might not be recorded
1925 RecordModules();
1927 m_stream.Close();
1931 //////////////////////////////////////////////////////////////////////////
1932 bool CMemReplay::EnterScope(EMemReplayAllocClass cls, EMemReplayUserPointerClass subCls, int moduleId)
1934 if (m_stream.IsOpen())
1936 m_scope.Lock();
1938 ++m_scopeDepth;
1940 if (m_scopeDepth == 1)
1942 m_scopeClass = cls;
1943 m_scopeSubClass = subCls;
1944 m_scopeModuleId = moduleId;
1947 return true;
1950 return false;
1953 void CMemReplay::ExitScope_Alloc(UINT_PTR id, UINT_PTR sz, UINT_PTR alignment)
1955 --m_scopeDepth;
1957 if (m_scopeDepth == 0)
1959 if (id && m_stream.IsOpen() && ThreadIsNotFlushing() && !g_memReplayPaused)
1961 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
1963 if (m_stream.IsOpen())
1965 UINT_PTR global = GetCurrentSysFree();
1966 INT_PTR changeGlobal = (INT_PTR)(s_replayLastGlobal - global);
1967 s_replayLastGlobal = global;
1969 RecordAlloc(m_scopeClass, m_scopeSubClass, m_scopeModuleId, id, alignment, sz, sz, changeGlobal);
1974 m_scope.Unlock();
1977 void CMemReplay::ExitScope_Realloc(UINT_PTR originalId, UINT_PTR newId, UINT_PTR sz, UINT_PTR alignment)
1979 if (!originalId)
1981 ExitScope_Alloc(newId, sz, alignment);
1982 return;
1985 if (!newId)
1987 ExitScope_Free(originalId);
1988 return;
1991 --m_scopeDepth;
1993 if (m_scopeDepth == 0)
1995 if (m_stream.IsOpen() && ThreadIsNotFlushing() && !g_memReplayPaused)
1997 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
1999 if (m_stream.IsOpen())
2001 UINT_PTR global = GetCurrentSysFree();
2002 INT_PTR changeGlobal = (INT_PTR)(s_replayLastGlobal - global);
2003 s_replayLastGlobal = global;
2005 RecordRealloc(m_scopeClass, m_scopeSubClass, m_scopeModuleId, originalId, newId, alignment, sz, sz, changeGlobal);
2010 m_scope.Unlock();
2013 void CMemReplay::ExitScope_Free(UINT_PTR id)
2015 --m_scopeDepth;
2017 if (m_scopeDepth == 0)
2019 if (id && m_stream.IsOpen() && ThreadIsNotFlushing() && !g_memReplayPaused)
2021 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2023 if (m_stream.IsOpen())
2025 UINT_PTR global = GetCurrentSysFree();
2026 INT_PTR changeGlobal = (INT_PTR)(s_replayLastGlobal - global);
2027 s_replayLastGlobal = global;
2029 PREFAST_SUPPRESS_WARNING(6326)
2030 RecordFree(m_scopeClass, m_scopeSubClass, m_scopeModuleId, id, changeGlobal);
2035 m_scope.Unlock();
2038 void CMemReplay::ExitScope()
2040 --m_scopeDepth;
2041 m_scope.Unlock();
2044 //////////////////////////////////////////////////////////////////////////
2045 void CMemReplay::AddLabel(const char* label)
2047 if (m_stream.IsOpen() && ThreadIsNotFlushing() && !g_memReplayPaused)
2049 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2051 if (m_stream.IsOpen())
2053 new(m_stream.AllocateRawEvent<MemReplayLabelEvent>(strlen(label)))MemReplayLabelEvent(label);
2058 void CMemReplay::AddLabelFmt(const char* labelFmt, ...)
2060 if (m_stream.IsOpen())
2062 va_list args;
2063 va_start(args, labelFmt);
2065 char msg[256];
2066 cry_vsprintf(msg, labelFmt, args);
2068 va_end(args);
2070 AddLabel(msg);
2074 //////////////////////////////////////////////////////////////////////////
2075 void CMemReplay::AddFrameStart()
2077 if (m_stream.IsOpen())
2079 static bool bSymbolsDumped = false;
2080 if (!bSymbolsDumped)
2082 DumpSymbols();
2083 bSymbolsDumped = true;
2086 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2088 if (m_stream.IsOpen())
2090 m_stream.WriteEvent(MemReplayFrameStartEvent(g_memReplayFrameCount++));
2095 //////////////////////////////////////////////////////////////////////////
2096 void CMemReplay::GetInfo(CryReplayInfo& infoOut)
2098 memset(&infoOut, 0, sizeof(CryReplayInfo));
2100 if (m_stream.IsOpen())
2102 infoOut.uncompressedLength = m_stream.GetUncompressedLength();
2103 infoOut.writtenLength = m_stream.GetWrittenLength();
2104 infoOut.trackingSize = m_stream.GetSize();
2105 infoOut.filename = m_stream.GetFilename();
2107 else
2109 ZeroStruct(infoOut);
2113 //////////////////////////////////////////////////////////////////////////
2114 void CMemReplay::AllocUsage(EMemReplayAllocClass allocClass, UINT_PTR id, UINT_PTR used)
2116 #if REPLAY_RECORD_USAGE_CHANGES
2117 if (!ptr)
2118 return;
2120 if (m_stream.IsOpen() && ThreadIsNotFlushing() && !g_memReplayPaused)
2122 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2123 if (m_stream.IsOpen())
2124 m_stream.WriteEvent(MemReplayAllocUsageEvent((uint16)allocClass, id, used));
2126 #endif
2129 void CMemReplay::AddAllocReference(void* ptr, void* ref)
2131 if (ptr && m_stream.IsOpen() && ThreadIsNotFlushing() && !g_memReplayPaused)
2133 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2135 if (m_stream.IsOpen())
2137 MemReplayAddAllocReferenceEvent* ev = m_stream.BeginAllocateRawEvent<MemReplayAddAllocReferenceEvent>(
2138 k_maxCallStackDepth * SIZEOF_MEMBER(MemReplayAddAllocReferenceEvent, callstack[0])
2139 - SIZEOF_MEMBER(MemReplayAddAllocReferenceEvent, callstack));
2141 new(ev) MemReplayAddAllocReferenceEvent(reinterpret_cast<UINT_PTR>(ptr), reinterpret_cast<UINT_PTR>(ref));
2143 WriteCallstack(ev->callstack, ev->callstackLength);
2144 m_stream.EndAllocateRawEvent<MemReplayAddAllocReferenceEvent>(ev->callstackLength * sizeof(ev->callstack[0]) - sizeof(ev->callstack));
2149 void CMemReplay::RemoveAllocReference(void* ref)
2151 if (ref && m_stream.IsOpen() && ThreadIsNotFlushing() && !g_memReplayPaused)
2153 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2155 if (m_stream.IsOpen())
2156 m_stream.WriteEvent(MemReplayRemoveAllocReferenceEvent(reinterpret_cast<UINT_PTR>(ref)));
2160 IMemReplay::FixedContextID CMemReplay::AddFixedContext(EMemStatContextType type, const char* str)
2162 const threadID threadId = CryGetCurrentThreadId();
2163 if (m_stream.IsOpen() && threadId != s_recordThreadId && !g_memReplayPaused)
2165 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2166 static IMemReplay::FixedContextID s_fixedContextCounter = 0;
2167 if (m_stream.IsOpen())
2169 FixedContextID id = s_fixedContextCounter++;
2170 new(m_stream.AllocateRawEvent<MemReplayAddFixedContextEvent>(strlen(str)))
2171 MemReplayAddFixedContextEvent(threadId, str, type, id);
2172 return id;
2175 return -1;
2178 void CMemReplay::PushFixedContext(FixedContextID id)
2180 const threadID threadId = CryGetCurrentThreadId();
2181 if (m_stream.IsOpen() && threadId != s_recordThreadId && !g_memReplayPaused)
2183 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2184 if (m_stream.IsOpen())
2186 m_stream.WriteEvent(MemReplayPushFixedContextEvent(threadId, id));
2191 void CMemReplay::PushContext(EMemStatContextType type, const char* str)
2193 if (m_stream.IsOpen() && ThreadIsNotFlushing())
2195 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2197 if (m_stream.IsOpen())
2199 new(m_stream.AllocateRawEvent<MemReplayPushContextEvent>(strlen(str)))
2200 MemReplayPushContextEvent(CryGetCurrentThreadId(), str, type);
2205 void CMemReplay::PushContextV(EMemStatContextType type, const char* format, va_list args)
2207 if (m_stream.IsOpen() && ThreadIsNotFlushing())
2209 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2211 if (m_stream.IsOpen())
2213 MemReplayPushContextEvent* ev = m_stream.BeginAllocateRawEvent<MemReplayPushContextEvent>(511);
2214 new(ev) MemReplayPushContextEvent(CryGetCurrentThreadId(), "", type);
2216 cry_vsprintf(ev->name, 512, format, args);
2218 m_stream.EndAllocateRawEvent<MemReplayPushContextEvent>(strlen(ev->name));
2223 //////////////////////////////////////////////////////////////////////////
2224 void CMemReplay::PopContext()
2226 if (m_stream.IsOpen() && ThreadIsNotFlushing())
2228 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2230 if (m_stream.IsOpen())
2232 m_stream.WriteEvent(MemReplayPopContextEvent(CryGetCurrentThreadId()));
2237 void CMemReplay::Flush()
2239 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2241 if (m_stream.IsOpen())
2242 m_stream.Flush();
2245 bool CMemReplay::EnableAsynchMode()
2247 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2249 if (m_stream.IsOpen())
2250 return m_stream.EnableAsynchMode();
2252 return false;
2255 void CMemReplay::MapPage(void* base, size_t size)
2257 if (m_stream.IsOpen() && base && size && !g_memReplayPaused)
2259 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2261 if (m_stream.IsOpen())
2263 MemReplayMapPageEvent* ev = m_stream.BeginAllocateRawEvent<MemReplayMapPageEvent>(
2264 k_maxCallStackDepth * sizeof(void*) - SIZEOF_MEMBER(MemReplayMapPageEvent, callstack));
2265 new(ev) MemReplayMapPageEvent(reinterpret_cast<UINT_PTR>(base), size);
2267 WriteCallstack(ev->callstack, ev->callstackLength);
2268 m_stream.EndAllocateRawEvent<MemReplayMapPageEvent>(sizeof(ev->callstack[0]) * ev->callstackLength - sizeof(ev->callstack));
2273 void CMemReplay::UnMapPage(void* base, size_t size)
2275 if (m_stream.IsOpen() && base && size && !g_memReplayPaused)
2277 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2279 if (m_stream.IsOpen())
2281 m_stream.WriteEvent(MemReplayUnMapPageEvent(reinterpret_cast<UINT_PTR>(base), size));
2286 void CMemReplay::RegisterFixedAddressRange(void* base, size_t size, const char* name)
2288 if (m_stream.IsOpen() && base && size && name && !g_memReplayPaused)
2290 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2292 if (m_stream.IsOpen())
2294 new(m_stream.AllocateRawEvent<MemReplayRegisterFixedAddressRangeEvent>(strlen(name)))
2295 MemReplayRegisterFixedAddressRangeEvent(reinterpret_cast<UINT_PTR>(base), size, name);
2300 void CMemReplay::MarkBucket(int bucket, size_t alignment, void* base, size_t length)
2302 if (m_stream.IsOpen() && base && length && !g_memReplayPaused)
2304 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2306 if (m_stream.IsOpen())
2307 m_stream.WriteEvent(MemReplayBucketMarkEvent(reinterpret_cast<UINT_PTR>(base), length, bucket, alignment));
2311 void CMemReplay::UnMarkBucket(int bucket, void* base)
2313 if (m_stream.IsOpen() && base && !g_memReplayPaused)
2315 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2317 if (m_stream.IsOpen())
2318 m_stream.WriteEvent(MemReplayBucketUnMarkEvent(reinterpret_cast<UINT_PTR>(base), bucket));
2322 void CMemReplay::BucketEnableCleanups(void* allocatorBase, bool enabled)
2324 if (m_stream.IsOpen() && allocatorBase && !g_memReplayPaused)
2326 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2328 if (m_stream.IsOpen())
2329 m_stream.WriteEvent(MemReplayBucketCleanupEnabledEvent(reinterpret_cast<UINT_PTR>(allocatorBase), enabled));
2333 void CMemReplay::MarkPool(int pool, size_t alignment, void* base, size_t length, const char* name)
2335 if (m_stream.IsOpen() && base && length && !g_memReplayPaused)
2337 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2339 if (m_stream.IsOpen())
2341 new(m_stream.AllocateRawEvent<MemReplayPoolMarkEvent>(strlen(name)))
2342 MemReplayPoolMarkEvent(reinterpret_cast<UINT_PTR>(base), length, pool, alignment, name);
2347 void CMemReplay::UnMarkPool(int pool, void* base)
2349 if (m_stream.IsOpen() && base && !g_memReplayPaused)
2351 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2353 if (m_stream.IsOpen())
2354 m_stream.WriteEvent(MemReplayPoolUnMarkEvent(reinterpret_cast<UINT_PTR>(base), pool));
2358 void CMemReplay::AddTexturePoolContext(void* ptr, int mip, int width, int height, const char* name, uint32 flags)
2360 if (m_stream.IsOpen() && ptr && !g_memReplayPaused)
2362 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2364 if (m_stream.IsOpen())
2366 new(m_stream.AllocateRawEvent<MemReplayTextureAllocContextEvent>(strlen(name)))
2367 MemReplayTextureAllocContextEvent(reinterpret_cast<UINT_PTR>(ptr), mip, width, height, flags, name);
2372 void CMemReplay::AddSizerTree(const char* name)
2374 MemReplayCrySizer sizer(m_stream, name);
2375 static_cast<CSystem*>(gEnv->pSystem)->CollectMemStats(&sizer);
2378 void CMemReplay::AddScreenshot()
2380 #if 0
2381 if (m_stream.IsOpen() && !g_memReplayPaused)
2383 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2385 if (m_stream.IsOpen())
2387 PREFAST_SUPPRESS_WARNING(6255)
2388 MemReplayScreenshotEvent * ev = new(alloca(sizeof(MemReplayScreenshotEvent) + 65536))MemReplayScreenshotEvent();
2389 gEnv->pRenderer->ScreenShotBuf(8, ev->bmp);
2390 m_stream.WriteEvent(ev, ev->bmp[0] * ev->bmp[1] * 3 + 3);
2393 #endif
2396 void CMemReplay::RegisterContainer(const void* key, int type)
2398 #if REPLAY_RECORD_CONTAINERS
2399 if (key)
2401 if (m_stream.IsOpen() && !g_memReplayPaused)
2403 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2405 if (m_stream.IsOpen())
2407 MemReplayRegisterContainerEvent* ev = m_stream.BeginAllocateRawEvent<MemReplayRegisterContainerEvent>(
2408 k_maxCallStackDepth * SIZEOF_MEMBER(MemReplayRegisterContainerEvent, callstack[0]) - SIZEOF_MEMBER(MemReplayRegisterContainerEvent, callstack));
2410 new(ev) MemReplayRegisterContainerEvent(reinterpret_cast<UINT_PTR>(key), type);
2412 WriteCallstack(ev->callstack, ev->callstackLength);
2413 m_stream.EndAllocateRawEvent<MemReplayRegisterContainerEvent>(length * sizeof(ev->callstack[0]) - sizeof(ev->callstack));
2417 #endif
2420 void CMemReplay::UnregisterContainer(const void* key)
2422 #if REPLAY_RECORD_CONTAINERS
2423 if (key)
2425 if (m_stream.IsOpen() && !g_memReplayPaused)
2427 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2429 if (m_stream.IsOpen())
2431 m_stream.WriteEvent(MemReplayUnregisterContainerEvent(reinterpret_cast<UINT_PTR>(key)));
2435 #endif
2438 void CMemReplay::BindToContainer(const void* key, const void* alloc)
2440 #if REPLAY_RECORD_CONTAINERS
2441 if (key && alloc)
2443 if (m_stream.IsOpen() && !g_memReplayPaused)
2445 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2447 if (m_stream.IsOpen())
2449 m_stream.WriteEvent(MemReplayBindToContainerEvent(reinterpret_cast<UINT_PTR>(key), reinterpret_cast<UINT_PTR>(alloc)));
2453 #endif
2456 void CMemReplay::UnbindFromContainer(const void* key, const void* alloc)
2458 #if REPLAY_RECORD_CONTAINERS
2459 if (key && alloc)
2461 if (m_stream.IsOpen() && !g_memReplayPaused)
2463 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2465 if (m_stream.IsOpen())
2467 m_stream.WriteEvent(MemReplayUnbindFromContainerEvent(reinterpret_cast<UINT_PTR>(key), reinterpret_cast<UINT_PTR>(alloc)));
2471 #endif
2474 void CMemReplay::SwapContainers(const void* keyA, const void* keyB)
2476 #if REPLAY_RECORD_CONTAINERS
2477 if (keyA && keyB && (keyA != keyB) && m_stream.IsOpen() && !g_memReplayPaused)
2479 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2481 if (m_stream.IsOpen())
2483 m_stream.WriteEvent(MemReplaySwapContainersEvent(reinterpret_cast<UINT_PTR>(keyA), reinterpret_cast<UINT_PTR>(keyB)));
2486 #endif
2489 void CMemReplay::RecordModuleLoad(void* pSelf, const CReplayModules::ModuleLoadDesc& mld)
2491 CMemReplay* pMR = reinterpret_cast<CMemReplay*>(pSelf);
2493 const char* baseName = strrchr(mld.name, '\\');
2494 if (!baseName)
2495 baseName = mld.name;
2497 pMR->m_stream.WriteEvent(MemReplayModuleRefEvent(mld.name, mld.path, mld.address, mld.size, mld.sig));
2499 pMR->PushContext(EMemStatContextType::Other, baseName);
2501 pMR->RecordAlloc(
2502 EMemReplayAllocClass::UserPointer, EMemReplayUserPointerClass::CryMalloc, eCryModule,
2503 (UINT_PTR)mld.address,
2504 4096,
2505 mld.size,
2506 mld.size,
2509 pMR->PopContext();
2512 void CMemReplay::RecordModuleUnload(void* pSelf, const CReplayModules::ModuleUnloadDesc& mld)
2514 CMemReplay* pMR = reinterpret_cast<CMemReplay*>(pSelf);
2516 PREFAST_SUPPRESS_WARNING(6326)
2517 pMR->RecordFree(EMemReplayAllocClass::UserPointer, EMemReplayUserPointerClass::CryMalloc, eCryModule, mld.address, 0);
2518 pMR->m_stream.WriteEvent(MemReplayModuleUnRefEvent(mld.address));
2521 void CMemReplay::RecordAlloc(EMemReplayAllocClass cls, EMemReplayUserPointerClass subCls, int moduleId, UINT_PTR p, UINT_PTR alignment, UINT_PTR sizeRequested, UINT_PTR sizeConsumed, INT_PTR sizeGlobal)
2523 MemReplayAllocEvent* ev = m_stream.BeginAllocateRawEvent<MemReplayAllocEvent>(
2524 k_maxCallStackDepth * sizeof(void*) - SIZEOF_MEMBER(MemReplayAllocEvent, callstack));
2526 new(ev) MemReplayAllocEvent(
2527 CryGetCurrentThreadId(),
2528 static_cast<uint16>(moduleId),
2529 static_cast<uint16>(cls),
2530 static_cast<uint16>(subCls),
2532 static_cast<uint32>(alignment),
2533 static_cast<uint32>(sizeRequested),
2534 static_cast<uint32>(sizeConsumed),
2535 static_cast<int32>(sizeGlobal));
2537 WriteCallstack(ev->callstack, ev->callstackLength);
2538 m_stream.EndAllocateRawEvent<MemReplayAllocEvent>(sizeof(ev->callstack[0]) * ev->callstackLength - sizeof(ev->callstack));
2541 void CMemReplay::RecordRealloc(EMemReplayAllocClass cls, EMemReplayUserPointerClass subCls, int moduleId, UINT_PTR originalId, UINT_PTR newId, UINT_PTR alignment, UINT_PTR sizeRequested, UINT_PTR sizeConsumed, INT_PTR sizeGlobal)
2543 MemReplayReallocEvent* ev = new(m_stream.BeginAllocateRawEvent<MemReplayReallocEvent>(
2544 k_maxCallStackDepth * SIZEOF_MEMBER(MemReplayReallocEvent, callstack[0]) - SIZEOF_MEMBER(MemReplayReallocEvent, callstack)))
2545 MemReplayReallocEvent(
2546 CryGetCurrentThreadId(),
2547 static_cast<uint16>(moduleId),
2548 static_cast<uint16>(cls),
2549 static_cast<uint16>(subCls),
2550 originalId,
2551 newId,
2552 static_cast<uint32>(alignment),
2553 static_cast<uint32>(sizeRequested),
2554 static_cast<uint32>(sizeConsumed),
2555 static_cast<int32>(sizeGlobal));
2557 WriteCallstack(ev->callstack, ev->callstackLength);
2558 m_stream.EndAllocateRawEvent<MemReplayReallocEvent>(ev->callstackLength * sizeof(ev->callstack[0]) - sizeof(ev->callstack));
2561 void CMemReplay::RecordFree(EMemReplayAllocClass cls, EMemReplayUserPointerClass subCls, int moduleId, UINT_PTR id, INT_PTR sizeGlobal)
2563 MemReplayFreeEvent* ev =
2564 new(m_stream.BeginAllocateRawEvent<MemReplayFreeEvent>(SIZEOF_MEMBER(MemReplayFreeEvent, callstack[0]) * k_maxCallStackDepth - SIZEOF_MEMBER(MemReplayFreeEvent, callstack)))
2565 MemReplayFreeEvent(
2566 CryGetCurrentThreadId(),
2567 static_cast<uint16>(moduleId),
2568 static_cast<uint16>(cls),
2569 static_cast<uint16>(subCls),
2571 static_cast<int32>(sizeGlobal));
2573 if (REPLAY_RECORD_FREECS)
2574 WriteCallstack(ev->callstack, ev->callstackLength);
2575 m_stream.EndAllocateRawEvent<MemReplayFreeEvent>(ev->callstackLength * sizeof(ev->callstack[0]) - sizeof(ev->callstack));
2578 void CMemReplay::RecordModules()
2580 MEMSTAT_CONTEXT(EMemStatContextType::Other, "DLL Image Size (incl. Data Segment)");
2581 m_modules.RefreshModules(RecordModuleLoad, RecordModuleUnload, this);
2584 void CMemReplay::WriteCallstack(UINT_PTR* callstack, uint32& length)
2586 if(g_cvars.memReplayRecordCallstacks)
2588 length = k_maxCallStackDepth;
2589 CSystem::debug_GetCallStackRaw(CastCallstack(callstack), length);
2591 else
2593 length = 1;
2594 *callstack = 0;
2598 void CMemReplay::WriteCallstack(UINT_PTR* callstack, uint16& length)
2600 uint32 len;
2601 WriteCallstack(callstack, len);
2602 length = len;
2605 MemReplayCrySizer::MemReplayCrySizer(ReplayLogStream& stream, const char* name)
2606 : m_stream(&stream)
2607 , m_totalSize(0)
2608 , m_totalCount(0)
2610 Push(name);
2613 MemReplayCrySizer::~MemReplayCrySizer()
2615 Pop();
2618 MEMREPLAY_SCOPE(EMemReplayAllocClass::UserPointer, EMemReplayUserPointerClass::Unknown);
2619 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2621 if (m_stream->IsOpen())
2623 // Clear the added objects set within the g_replayProcessed lock,
2624 // as memReplay won't have seen the allocations made by it.
2625 m_addedObjects.clear();
2630 size_t MemReplayCrySizer::GetTotalSize()
2632 return m_totalSize;
2635 size_t MemReplayCrySizer::GetObjectCount()
2637 return m_totalCount;
2640 void MemReplayCrySizer::Reset()
2642 m_totalSize = 0;
2643 m_totalCount = 0;
2646 bool MemReplayCrySizer::AddObject(const void* pIdentifier, size_t nSizeBytes, int nCount)
2648 bool ret = false;
2650 if (m_stream->IsOpen())
2652 MEMREPLAY_SCOPE(EMemReplayAllocClass::UserPointer, EMemReplayUserPointerClass::Unknown);
2653 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2655 if (m_stream->IsOpen())
2657 std::set<const void*>::iterator it = m_addedObjects.find(pIdentifier);
2658 if (it == m_addedObjects.end())
2660 m_totalSize += nSizeBytes;
2661 m_totalCount += nCount;
2662 m_addedObjects.insert(pIdentifier);
2663 m_stream->WriteEvent(MemReplaySizerAddRangeEvent((UINT_PTR)pIdentifier, nSizeBytes, nCount));
2664 ret = true;
2669 return ret;
2672 static NullResCollector s_nullCollectorMemreplay;
2674 IResourceCollector* MemReplayCrySizer::GetResourceCollector()
2676 return &s_nullCollectorMemreplay;
2679 void MemReplayCrySizer::SetResourceCollector(IResourceCollector*)
2683 void MemReplayCrySizer::Push(const char* szComponentName)
2685 if (m_stream->IsOpen())
2687 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2689 if (m_stream->IsOpen())
2691 new(m_stream->AllocateRawEvent<MemReplaySizerPushEvent>(strlen(szComponentName)))MemReplaySizerPushEvent(szComponentName);
2696 void MemReplayCrySizer::PushSubcomponent(const char* szSubcomponentName)
2698 Push(szSubcomponentName);
2701 void MemReplayCrySizer::Pop()
2703 if (m_stream->IsOpen())
2705 CryAutoLock<CryCriticalSection> lock(GetLogMutex());
2707 if (m_stream->IsOpen())
2709 m_stream->WriteEvent(MemReplaySizerPopEvent());
2714 #endif //CAPTURE_REPLAY_LOG