1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
4 #include <CryCore/BitFiddling.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"
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"
38 #if CRY_PLATFORM_DURANGO
39 #include <processthreadsapi.h>
42 #if CRY_PLATFORM_LINUX || CRY_PLATFORM_ANDROID
44 #if !defined(_GNU_SOURCE)
50 #if CRY_PLATFORM_LINUX || CRY_PLATFORM_ANDROID || CRY_PLATFORM_APPLE
54 #if CRY_PLATFORM_ORBIS
56 #include <CryNetwork/CrySocks.h>
59 #if CAPTURE_REPLAY_LOG || ENABLE_STATOSCOPE
64 struct MemReplayFrameStartEvent
66 static const int EventId
= MemReplayEventIds::RE_FrameStart
;
70 MemReplayFrameStartEvent(uint32 frameId
)
75 struct MemReplayLabelEvent
77 static const int EventId
= MemReplayEventIds::RE_Label
;
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()
88 struct MemReplayAddFixedContextEvent
90 static const int EventId
= MemReplayEventIds::RE_AddFixedContext
;
94 uint32 fixedContextId
;
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()
107 struct MemReplayPushFixedContextEvent
109 static const int EventId
= MemReplayEventIds::RE_PushFixedContext
;
112 IMemReplay::FixedContextID contextId
;
114 MemReplayPushFixedContextEvent(uint64 threadId
, IMemReplay::FixedContextID contextId
)
116 this->threadId
= threadId
;
117 this->contextId
= contextId
;
121 struct MemReplayPushContextEvent
123 static const int EventId
= MemReplayEventIds::RE_PushContext3
;
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.
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()
142 struct MemReplayPopContextEvent
144 static const int EventId
= MemReplayEventIds::RE_PopContext
;
148 explicit MemReplayPopContextEvent(uint64 threadId
)
150 this->threadId
= threadId
;
154 struct MemReplayModuleRefEvent
156 static const int EventId
= MemReplayEventIds::RE_ModuleRef
;
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
;
174 struct MemReplayModuleUnRefEvent
176 static const int EventId
= MemReplayEventIds::RE_ModuleUnRef
;
180 MemReplayModuleUnRefEvent(UINT_PTR address
)
181 : address(address
) {}
184 struct MemReplayModuleRefShortEvent
186 static const int EventId
= MemReplayEventIds::RE_ModuleShortRef
;
190 MemReplayModuleRefShortEvent(const char* name
)
192 cry_strcpy(this->name
, name
);
196 struct MemReplayAllocEvent
198 static const int EventId
= MemReplayEventIds::RE_Alloc6
;
203 uint32 sizeRequested
;
205 int32 sizeGlobal
; // Inferred from changes in global memory status
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
)
216 , alignment(alignment
)
217 , sizeRequested(sizeReq
)
218 , sizeConsumed(sizeCon
)
219 , sizeGlobal(sizeGlobal
)
221 , allocClass(allocClass
)
222 , allocSubClass(allocSubClass
)
228 struct MemReplayFreeEvent
230 static const int EventId
= MemReplayEventIds::RE_Free6
;
234 int32 sizeGlobal
; // Inferred from changes in global memory status
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
)
246 , sizeGlobal(sizeGlobal
)
248 , allocClass(allocClass
)
249 , allocSubClass(allocSubClass
)
255 struct MemReplayInfoEvent
257 static const int EventId
= MemReplayEventIds::RE_Info3
;
259 uint32 executableSize
;
260 uint32 initialGlobalSize
;
263 MemReplayInfoEvent(uint32 executableSize
, uint32 initialGlobalSize
, uint32 bucketsFree
)
264 : executableSize(executableSize
)
265 , initialGlobalSize(initialGlobalSize
)
266 , bucketsFree(bucketsFree
)
271 struct MemReplayAddressProfileEvent
273 static const int EventId
= MemReplayEventIds::RE_AddressProfile2
;
278 MemReplayAddressProfileEvent(UINT_PTR rsxStart
, uint32 rsxLength
)
280 , rsxLength(rsxLength
)
285 struct MemReplayAllocUsageEvent
287 static const int EventId
= MemReplayEventIds::RE_AllocUsage2
;
293 MemReplayAllocUsageEvent(uint16 allocClass
, UINT_PTR id
, uint32 used
)
294 : allocClass(allocClass
)
301 struct MemReplayScreenshotEvent
303 static const int EventId
= MemReplayEventIds::RE_Screenshot
;
307 MemReplayScreenshotEvent()
312 struct MemReplaySizerPushEvent
314 static const int EventId
= MemReplayEventIds::RE_SizerPush
;
318 MemReplaySizerPushEvent(const char* name
)
320 strcpy(this->name
, name
);
324 struct MemReplaySizerPopEvent
326 static const int EventId
= MemReplayEventIds::RE_SizerPop
;
329 struct MemReplaySizerAddRangeEvent
331 static const int EventId
= MemReplayEventIds::RE_SizerAddRange
;
337 MemReplaySizerAddRangeEvent(const UINT_PTR address
, uint32 size
, int32 count
)
345 struct MemReplayBucketMarkEvent
347 static const int EventId
= MemReplayEventIds::RE_BucketMark2
;
354 MemReplayBucketMarkEvent(UINT_PTR address
, uint32 length
, int32 index
, uint32 alignment
)
358 , alignment(alignment
)
363 struct MemReplayBucketUnMarkEvent
365 static const int EventId
= MemReplayEventIds::RE_BucketUnMark2
;
370 MemReplayBucketUnMarkEvent(UINT_PTR address
, int32 index
)
376 struct MemReplayAddAllocReferenceEvent
378 static const int EventId
= MemReplayEventIds::RE_AddAllocReference
;
381 UINT_PTR referenceId
;
382 uint32 callstackLength
;
383 UINT_PTR callstack
[1];
385 MemReplayAddAllocReferenceEvent(UINT_PTR address
, UINT_PTR referenceId
)
387 , referenceId(referenceId
)
393 struct MemReplayRemoveAllocReferenceEvent
395 static const int EventId
= MemReplayEventIds::RE_RemoveAllocReference
;
397 UINT_PTR referenceId
;
399 MemReplayRemoveAllocReferenceEvent(UINT_PTR referenceId
)
400 : referenceId(referenceId
)
405 struct MemReplayPoolMarkEvent
407 static const int EventId
= MemReplayEventIds::RE_PoolMark2
;
415 MemReplayPoolMarkEvent(UINT_PTR address
, uint32 length
, int32 index
, uint32 alignment
, const char* name
)
419 , alignment(alignment
)
421 strcpy(this->name
, name
);
426 struct MemReplayPoolUnMarkEvent
428 static const int EventId
= MemReplayEventIds::RE_PoolUnMark
;
433 MemReplayPoolUnMarkEvent(UINT_PTR address
, int32 index
)
439 struct MemReplayTextureAllocContextEvent
441 static const int EventId
= MemReplayEventIds::RE_TextureAllocContext2
;
450 MemReplayTextureAllocContextEvent(UINT_PTR ptr
, uint32 mip
, uint32 width
, uint32 height
, uint32 flags
, const char* name
)
457 strcpy(this->name
, name
);
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)
475 struct MemReplayReallocEvent
477 static const int EventId
= MemReplayEventIds::RE_Realloc3
;
483 uint32 newSizeRequested
;
484 uint32 newSizeConsumed
;
485 int32 sizeGlobal
; // Inferred from changes in global memory status
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
)
498 , alignment(alignment
)
499 , newSizeRequested(newSizeReq
)
500 , newSizeConsumed(newSizeCon
)
501 , sizeGlobal(sizeGlobal
)
503 , allocClass(allocClass
)
504 , allocSubClass(allocSubClass
)
510 struct MemReplayRegisterContainerEvent
512 static const int EventId
= MemReplayEventIds::RE_RegisterContainer
;
517 uint32 callstackLength
;
518 UINT_PTR callstack
[1]; // Must be last
520 MemReplayRegisterContainerEvent(UINT_PTR key
, uint32 type
)
529 struct MemReplayUnregisterContainerEvent
531 static const int EventId
= MemReplayEventIds::RE_UnregisterContainer
;
535 explicit MemReplayUnregisterContainerEvent(UINT_PTR key
)
542 struct MemReplayBindToContainerEvent
544 static const int EventId
= MemReplayEventIds::RE_BindToContainer
;
549 MemReplayBindToContainerEvent(UINT_PTR key
, UINT_PTR ptr
)
557 struct MemReplayUnbindFromContainerEvent
559 static const int EventId
= MemReplayEventIds::RE_UnbindFromContainer
;
564 MemReplayUnbindFromContainerEvent(UINT_PTR key
, UINT_PTR ptr
)
572 struct MemReplayRegisterFixedAddressRangeEvent
574 static const int EventId
= MemReplayEventIds::RE_RegisterFixedAddressRange
;
581 MemReplayRegisterFixedAddressRangeEvent(UINT_PTR address
, uint32 length
, const char* name
)
585 strcpy(this->name
, name
);
589 struct MemReplaySwapContainersEvent
591 static const int EventId
= MemReplayEventIds::RE_SwapContainers
;
596 MemReplaySwapContainersEvent(UINT_PTR keyA
, UINT_PTR keyB
)
603 struct MemReplayMapPageEvent
605 static const int EventId
= MemReplayEventIds::RE_MapPage2
;
609 uint32 callstackLength
;
610 UINT_PTR callstack
[1];
612 MemReplayMapPageEvent(UINT_PTR address
, uint32 length
)
620 struct MemReplayUnMapPageEvent
622 static const int EventId
= MemReplayEventIds::RE_UnMapPage
;
627 MemReplayUnMapPageEvent(UINT_PTR address
, uint32 length
)
636 ReplayDiskWriter::ReplayDiskWriter(const char* pSuffix
)
640 if (pSuffix
== NULL
|| strcmp(pSuffix
, "") == 0)
644 struct tm
* lt
= localtime(&curTime
);
646 strftime(m_filename
, CRY_ARRAY_COUNT(m_filename
), "memlog-%Y%m%d-%H%M%S.zmrl", lt
);
650 cry_sprintf(m_filename
, "memlog-%s.zmrl", pSuffix
);
654 bool ReplayDiskWriter::Open()
656 #ifdef USE_FILE_HANDLE_CACHE
659 char fn
[MAX_PATH
] = "";
660 #if CRY_PLATFORM_ORBIS
661 cry_strcpy(fn
, "/data/");
663 cry_strcat(fn
, m_filename
);
664 m_fp
= ::fopen(fn
, "wb");
667 #if defined(USE_FILE_HANDLE_CACHE)
668 #define fopen WrappedFopen
672 void ReplayDiskWriter::Close()
674 #ifdef USE_FILE_HANDLE_CACHE
685 #if defined(USE_FILE_HANDLE_CACHE)
686 #define fclose WrappedFclose
690 int ReplayDiskWriter::Write(const void* data
, size_t sz
, size_t n
)
692 #ifdef USE_FILE_HANDLE_CACHE
695 int res
= ::fwrite(data
, sz
, n
, m_fp
);
696 #if defined(USE_FILE_HANDLE_CACHE)
697 #define fwrite WrappedFWrite
700 m_written
+= res
* sz
;
705 void ReplayDiskWriter::Flush()
707 #ifdef USE_FILE_HANDLE_CACHE
713 #if defined(USE_FILE_HANDLE_CACHE)
714 #define fflush WrappedFflush
718 ReplaySocketWriter::ReplaySocketWriter(const char* address
)
720 , m_sock(CRY_INVALID_SOCKET
)
723 #if defined(REPLAY_SOCKET_WRITER)
724 const char* portStart
= strchr(address
, ':');
726 m_port
= atoi(portStart
+ 1);
728 portStart
= address
+ strlen(address
);
730 cry_strcpy(m_address
, address
, (size_t)(portStart
- address
));
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
743 PREFAST_SUPPRESS_WARNING(6031); // we don't need wsaData
744 WSAStartup(MAKEWORD(2, 2), &wsaData
); // ensure CrySock::socket has been initialized
747 addr32
= CrySock::GetAddrForHostOrIP(m_address
, 0);
752 m_sock
= CrySock::socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
753 if (m_sock
== CRY_INVALID_SOCKET
)
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
;
775 #endif //#if defined(REPLAY_SOCKET_WRITER)
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
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
;
805 int sent
= send(m_sock
, bdata
, toSend
, 0);
807 if (sent
== CRY_SOCKET_ERROR
)
820 #endif //#if defined(REPLAY_SOCKET_WRITER)
825 void ReplaySocketWriter::Flush()
831 #if CAPTURE_REPLAY_LOG
833 typedef CryLockT
<CRYLOCK_RECURSIVE
> MemFastMutex
;
834 static MemFastMutex
& GetLogMutex()
836 static MemFastMutex 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
);
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
));
918 void* ReplayAllocatorBase::MapAddressSpace(void* addr
, size_t sz
)
920 int res
= mprotect(addr
, sz
, PROT_READ
| PROT_WRITE
);
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
);
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
);
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
);
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");
999 pSuffix
+= strlen("-memreplaysuffix");
1002 if (*pSuffix
== '\0' || *pSuffix
== '+' || *pSuffix
== '-')
1007 if (*pSuffix
!= ' ')
1011 if (pSuffix
!= NULL
)
1013 const char* pEnd
= pSuffix
;
1014 while (*pEnd
!= '\0' && *pEnd
!= ' ')
1016 cry_strcpy(pSuffixBuffer
, bufferLength
, pSuffix
, (size_t)(pEnd
- pSuffix
));
1022 ReplayAllocator::ReplayAllocator()
1024 m_heap
= ReserveAddressSpace(MaxSize
);
1028 m_commitEnd
= m_heap
;
1029 m_allocEnd
= m_heap
;
1032 ReplayAllocator::~ReplayAllocator()
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
);
1051 m_commitEnd
= reinterpret_cast<void*>(newCommitEnd
);
1054 void* ReplayAllocator::Allocate(size_t sz
)
1056 CryAutoLock
<CryCriticalSectionNonRecursive
> lock(m_lock
);
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
)
1070 LPVOID res
= MapAddressSpace(m_commitEnd
, alignedEnd
- reinterpret_cast<uint8
*>(m_commitEnd
));
1074 m_commitEnd
= alignedEnd
;
1077 m_allocEnd
= newEnd
;
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
)
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
;
1114 void ReplayCompressor::Flush()
1119 void ReplayCompressor::Write(const uint8
* data
, size_t len
)
1121 if (CompressTargetSize
< len
)
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;
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);
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
;
1183 void ReplayRecordThread::Flush()
1187 while (m_nextCommand
!= CMD_Idle
)
1194 m_nextCommand
= CMD_Flush
;
1196 m_cond
.NotifySingle();
1199 void ReplayRecordThread::Write(const uint8
* data
, size_t len
)
1203 while (m_nextCommand
!= CMD_Idle
)
1212 m_nextCommand
= CMD_Write
;
1214 m_cond
.NotifySingle();
1217 void ReplayRecordThread::ThreadEntry()
1219 RecordingThreadScope recordScope
;
1225 while (m_nextCommand
== CMD_Idle
)
1228 switch (m_nextCommand
)
1232 m_nextCommand
= CMD_Idle
;
1237 size_t length
= m_nextLength
;
1238 const uint8
* data
= m_nextData
;
1243 m_compressor
->Write(data
, length
);
1248 m_compressor
->Flush();
1252 m_nextCommand
= CMD_Idle
;
1258 void ReplayRecordThread::SignalStopWork()
1262 while (m_nextCommand
!= CMD_Idle
)
1269 m_nextCommand
= CMD_Stop
;
1270 m_cond
.NotifySingle();
1272 while (m_nextCommand
!= CMD_Idle
)
1284 ReplayLogStream::ReplayLogStream()
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)
1301 ReplayLogStream::~ReplayLogStream()
1305 CryAutoLock
<CryCriticalSection
> lock(GetLogMutex());
1310 bool ReplayLogStream::Open(const char* openString
)
1314 m_allocator
.Reserve(512 * 1024);
1316 while (isspace((unsigned char) *openString
))
1319 const char* sep
= strchr(openString
, ':');
1321 sep
= openString
+ strlen(openString
);
1323 char destination
[32];
1324 const char* arg
= "";
1326 if (sep
!= openString
)
1328 while (isspace((unsigned char) sep
[-1]))
1331 cry_strcpy(destination
, openString
, (size_t)(sep
- openString
));
1338 while (isspace((unsigned char) *arg
))
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();
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
);
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
;
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
);
1405 void ReplayLogStream::Close()
1409 // Flag it as closed here, to prevent recursion into the memory logging as it is closing.
1410 CryInterlockedDecrement(&m_isOpen
);
1416 #if REPLAY_RECORD_THREADED
1419 m_recordThread
->SignalStopWork();
1420 gEnv
->pThreadManager
->JoinThread(m_recordThread
, eJM_Join
);
1421 m_recordThread
->~ReplayRecordThread();
1422 m_recordThread
= NULL
;
1428 m_compressor
->~ReplayCompressor();
1429 m_compressor
= NULL
;
1442 bool ReplayLogStream::EnableAsynchMode()
1444 #if REPLAY_RECORD_THREADED
1445 // System to create thread is not running yet
1446 if (!gEnv
|| !gEnv
->pSystem
)
1449 // Asynch mode already enabled
1453 // Write out pending data
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
;
1472 void ReplayLogStream::Flush()
1474 if (m_writer
!= NULL
)
1476 m_uncompressedLen
+= m_bufferEnd
- &m_buffer
[0];
1478 #if REPLAY_RECORD_THREADED
1481 m_recordThread
->Write(&m_buffer
[0], m_bufferEnd
- &m_buffer
[0]);
1482 m_buffer
= (m_buffer
== m_bufferA
) ? m_bufferB
: m_bufferA
;
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
1500 m_recordThread
->Flush();
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();
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];
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");
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
)))
1595 cry_strcpy(pdbPath
, szModName
);
1596 char* pPdbPathExt
= strrchr(pdbPath
, '.');
1598 strcpy(pPdbPathExt
, ".pdb");
1600 cry_strcat(pdbPath
, ".pdb");
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
];
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
);
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],
1644 cry_strcpy(mld
.name
, szModName
);
1645 cry_strcpy(mld
.path
, pdbPath
);
1646 mld
.address
= (UINT_PTR
)modInfo
.lpBaseOfDll
;
1647 mld
.size
= modInfo
.SizeOfImage
;
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();
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
;
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
);
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
);
1695 cry_strcpy(pdbPath
, moduleName
);
1696 char* pPdbPathExt
= strrchr(pdbPath
, '.');
1698 strcpy(pPdbPathExt
, ".pdb");
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
];
1721 CVDebugInfo
* pCVInfo
= (CVDebugInfo
*)((char*)moduleBase
+ dir
.AddressOfRawData
);
1722 ihModInfo
.PdbSig70
= pCVInfo
->pdbSig
;
1723 ihModInfo
.PdbAge
= pCVInfo
->age
;
1724 cry_strcpy(pdbPath
, pCVInfo
->pdbName
);
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],
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
);
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
);
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;
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
);
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;
1790 new(&g_memReplay
)CMemReplay();
1794 return alias_cast
<CMemReplay
*>(g_memReplay
);
1797 CMemReplay::CMemReplay()
1798 : m_allocReference(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())
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.
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
))
1856 cry_strcpy(openCmd
, arg
, (size_t)(argEnd
- arg
));
1860 RetrieveMemReplaySuffix(openCmd
+ 5, lwrCmdLine
.c_str(), bufferLength
- 5);
1863 Start(bPaused
, openCmd
);
1867 //////////////////////////////////////////////////////////////////////////
1868 void CMemReplay::Start(bool bPaused
, const char* 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";
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();
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"));
1911 #if CRY_PLATFORM_ORBIS
1912 m_stream
.WriteEvent(MemReplayModuleRefShortEvent("orbis"));
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
1931 //////////////////////////////////////////////////////////////////////////
1932 bool CMemReplay::EnterScope(EMemReplayAllocClass cls
, EMemReplayUserPointerClass subCls
, int moduleId
)
1934 if (m_stream
.IsOpen())
1940 if (m_scopeDepth
== 1)
1943 m_scopeSubClass
= subCls
;
1944 m_scopeModuleId
= moduleId
;
1953 void CMemReplay::ExitScope_Alloc(UINT_PTR id
, UINT_PTR sz
, UINT_PTR alignment
)
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
);
1977 void CMemReplay::ExitScope_Realloc(UINT_PTR originalId
, UINT_PTR newId
, UINT_PTR sz
, UINT_PTR alignment
)
1981 ExitScope_Alloc(newId
, sz
, alignment
);
1987 ExitScope_Free(originalId
);
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
);
2013 void CMemReplay::ExitScope_Free(UINT_PTR id
)
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
);
2038 void CMemReplay::ExitScope()
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())
2063 va_start(args
, labelFmt
);
2066 cry_vsprintf(msg
, labelFmt
, args
);
2074 //////////////////////////////////////////////////////////////////////////
2075 void CMemReplay::AddFrameStart()
2077 if (m_stream
.IsOpen())
2079 static bool bSymbolsDumped
= false;
2080 if (!bSymbolsDumped
)
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();
2109 ZeroStruct(infoOut
);
2113 //////////////////////////////////////////////////////////////////////////
2114 void CMemReplay::AllocUsage(EMemReplayAllocClass allocClass
, UINT_PTR id
, UINT_PTR used
)
2116 #if REPLAY_RECORD_USAGE_CHANGES
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
));
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
);
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())
2245 bool CMemReplay::EnableAsynchMode()
2247 CryAutoLock
<CryCriticalSection
> lock(GetLogMutex());
2249 if (m_stream
.IsOpen())
2250 return m_stream
.EnableAsynchMode();
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()
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);
2396 void CMemReplay::RegisterContainer(const void* key
, int type
)
2398 #if REPLAY_RECORD_CONTAINERS
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
));
2420 void CMemReplay::UnregisterContainer(const void* key
)
2422 #if REPLAY_RECORD_CONTAINERS
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
)));
2438 void CMemReplay::BindToContainer(const void* key
, const void* alloc
)
2440 #if REPLAY_RECORD_CONTAINERS
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
)));
2456 void CMemReplay::UnbindFromContainer(const void* key
, const void* alloc
)
2458 #if REPLAY_RECORD_CONTAINERS
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
)));
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
)));
2489 void CMemReplay::RecordModuleLoad(void* pSelf
, const CReplayModules::ModuleLoadDesc
& mld
)
2491 CMemReplay
* pMR
= reinterpret_cast<CMemReplay
*>(pSelf
);
2493 const char* baseName
= strrchr(mld
.name
, '\\');
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
);
2502 EMemReplayAllocClass::UserPointer
, EMemReplayUserPointerClass::CryMalloc
, eCryModule
,
2503 (UINT_PTR
)mld
.address
,
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
),
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
)))
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
);
2598 void CMemReplay::WriteCallstack(UINT_PTR
* callstack
, uint16
& length
)
2601 WriteCallstack(callstack
, len
);
2605 MemReplayCrySizer::MemReplayCrySizer(ReplayLogStream
& stream
, const char* name
)
2613 MemReplayCrySizer::~MemReplayCrySizer()
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()
2635 size_t MemReplayCrySizer::GetObjectCount()
2637 return m_totalCount
;
2640 void MemReplayCrySizer::Reset()
2646 bool MemReplayCrySizer::AddObject(const void* pIdentifier
, size_t nSizeBytes
, int nCount
)
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
));
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