Mem pro 是一个主要集成内存泄露检测的工具,其具有自身的源码和GUI,在GUI中利用”Launch” button进行加载自己待检测的application,目前支持的平台为Windows,Unix, Linux, OSX, IOS, GCC;但是按照官网的说法,其虽然只能运行到WIN上,但是根据TCP协议传输dump的方式也可以和其他平台的app进行连接;
关于内存泄露,按照官方文档中的说法,其检测内存泄露的算法主要是两种,一种是在抓取dump时候未被引用的变量会被认定为泄露,一种是在抓取的时候频繁进行的堆栈被认为是可疑的泄露;由于后者需要平台相关的callstack堆栈抓取支持,所以现在从其代码看来,还不支持PS4和XBoxOne, 这部分的官网地址为:http://www.puredevsoftware.com/MemProUserGuide/LeaksView.htm ;
关于支持的平台:Windows, Unix, Linux, OSX, iOS, gcc; Note: MemPro only runs on Windows but can connect to apps on these other platforms;
其官方网址为:http://www.puredevsoftware.com/mempro/index.htm
其官方文档为:http://www.puredevsoftware.com/MemProUserGuide/Introduction.htm
安装完GUI软件可以找到相关源码和测试程序,其readme简单介绍了使用方法,内容为:
-----------------------------------------------------------------------
MemPro
----------------------------------------------------------------------- Copyright (c) , All Right Reserved
Author: Stewart Lynch
date: //
Website: www.puredevsoftware.com
Version: 1.3.7.0 -----------------------------------------------------------------------
MemPro is a Windows based native memory profiler for C++. This README is taken from the setup page in the help chm file. ------------------------------------
Setup To allow MemPro to connect with your application you must compile your app
with the MemProLib code and call the tracking functions when allocating
memory. There are three main ways you can do this: * Include the MemPro.cpp file in one of your source cpp files
* Compile and Link against the MemProLib.lib
* Link with MemProDll.dll The MemProLib library can be found in the source code installation folder
that was set in the installation, the default is:
C:\Program Files\PureDevSoftware\MemPro Note: if you are profiling an application with multiple dlls you must use
the third option and link MemProDll.dll. Note: If you are including MemPro.cpp you do NOT need to link to the static library. ------------------------------------
Including MemPro.cpp This is the recommended way of embedding the code. It is simple, and easy to comment
out when not needed. It is also the most portable solution. Add these three lines of code to your main cpp source file: //#define WAIT_FOR_CONNECT true
#define OVERRIDE_NEW_DELETE
#include "MemPro.cpp" <-- include the CPP (yes, you are including the cpp file) the cpp file can be found here:
C:\Program Files\PureDevSoftware\MemPro\MemProLib\src The WAIT_FOR_CONNECT line is optional. If defined to true your application will block
on startup until MemPro has connected. This allows you to be sure that you are tracking
every single allocation. And that's it! ------------------------------------
Linking to the MemProLib library If you prefer to link to the MemPro library, include the MemPro.hpp in your main.cpp //#define WAIT_FOR_CONNECT true
#define OVERRIDE_NEW_DELETE
#include "MemPro.hpp" <-- include the HPP And link to one of the pre-built libraries in the lib folder. the header file can be found here:
C:\Program Files\PureDevSoftware\MemPro\MemProLib\src The lib files can be found here
C:\Program Files\PureDevSoftware\MemPro\MemProLib\lib If none of the pre-built libraries are suitable you can build your own MemProLib from
the source code in the src file. MemProLib builds on virtually any platform and OS. The
MemProLib source code can be found here:
C:\Program Files\PureDevSoftware\MemPro\MemProLib\src ------------------------------------
Linking to the MemProLib Dll Add these two lines to a cpp source file in each dll that you want to profile #define OVERRIDE_NEW_DELETE
Include "MemProDll.hpp" Copy the MemProDLL.dll file to your exe directory. The header file can be found here:
C:\Program Files\PureDevSoftware\MemPro\MemProDll\include The dll and lib files are here:
C:\Program Files\PureDevSoftware\MemPro\MemProDll\bin MemProDll.hpp includes a pragma telling the linker to link to MemProDll.lib. This
adds a dependency to the dll. Please make sure the linker can find the lib file
by adding the necessary path or modifying the pragma. Note: Make sure that MemProDll.hpp is included only _once_ in each dll. Put it in
the main source file, do not put it into a header that is included in multiple places. ------------------------------------
Include and Library Paths The installer automatically adds the include and library paths to the INCLUDE and
LIB environment variables. However, the IDE does not use these variables unless
run with the /useenv command line switch. So you'll probably want to add the path
in the global property sheet in the IDE. ------------------------------------
If you have already overridden new and delete In the above solutions the OVERRIDE_NEW_DELETE define tells MemPro to override new
and delete with its tracking hooks inserted. If you have already overridden
new and delete (maybe for your own memory manager) you will need to insert the
MemPro hooks into your overridden functions: Include MemPro.cpp as before but don't define OVERRIDE_NEW_DELETE. Instead use the
MemPro::TrackAlloc and MemPro::TrackFree functions #include "MemPro.hpp" // or MemProDll.hpp if linking to MemProDll.dll bool g_WaitForConnect = false; void* operator new(size_t size)
{
void* p = YourInternalAlloc(size);
MemPro::TrackAlloc(p, size, g_WaitForConnect);
return p;
} void operator delete(void* p)
{
MemPro::TrackFree(p, g_WaitForConnect);
YourInternalFree(p);
} void* operator new[](size_t size)
{
void* p = YourInternalAlloc(size);
MemPro::TrackAlloc(p, size, g_WaitForConnect);
return p;
} void operator delete[](void* p)
{
MemPro::TrackFree(p, g_WaitForConnect);
YourInternalFree(p);
} The g_WaitForConnect bool specifies whether the call blocks until the MemPro app has
connected. This is useful for making sure absolutely every allocation is being tracked. ------------------------------------
What if I can't use a winsock connection in my app? MemProLib uses a winsock TCP connection to communicate with MemPro. If for some reason
this isn't possible in your application (no network connection for example) then you
can still use MemPro. Instead of connecting to your application over TCP you can tell MemProLib to write out
a dump file of all allocations. This dump file can then be opened in MemPro and analyised
just like any other mempro file. To enable allocation dumping uncomment this line in MemPro.hpp //#define WRITE_DUMP _T("allocs.mempro_dump") When this define is enabled the specified file will be created and all operations will be written
to this file instead of being set over the network. The define also removes any dependency on
windows sockets, so you should be able to use it in any environment. This file can get pretty
big, so please ensure you have enough disk space. Note: To avoid linker errors when overriding new/delete, make sure that MemPro.cpp is included
before any system headers that also override new/delete. You may need to turn off any pre-compiled
headers for that file. You may also need to do a full re-compile of your app to avoid multiple
defined new and delete function warnings. ------------------------------------
If you have any problems setting up MemProLib: * please read the Help chm document
* see the FAQ on the website http://www.puredevsoftware.com/mempro_faq.htm
* email slynch@puredevsoftware.com Stewart Lynch
PureDev Software.
===============================================================================================================================================================
关于泄露检测方面,官方给出信息:
MemPro tracks leaks in three ways:
- Allocations that are not freed on exit.
- Allocations that are not referenced by anything else in the process.
- Allocation callstacks that have specific memory allocation patterns
Unreferenced Allocations
Unreferenced allocations are:
Any allocations that have not been freed when the app are exited
Any allocations that are not referenced by anything else in memory
(note: To use this feature you must take a full snapshot).
The latter type of unreferenced allocations
can be detected while the application is still running. MemPro will take a
snapshot of the entire process memory and scan it for pointers to allocations.
Any allocations that MemPro can't find a pointer to are considered as leaked.
Suspected Leaks
MemPro analyses the allocation patterns of
all callstacks. Allocations are grouped by the callstack. Simply put, if a
callstack is constantly allocating memory and never freeing it then the
callstack graph will look like a diagonal line (bottom left to top right).
MemPro uses a set of heuristics to analyse the callstack graph to see how much
it looks like a leak and then gives that callstack a score from 0 to 100, 100
being a definite leak. Callstack graphs are normalised, so only the shape of
the graph should be taken into consideration.
MemPro will probably bring up some false
positives, to eliminate these you can use the exclusion filters discussed below
and the minimum leak score.
By default MemPro will only list the first
500 allocations. This is to keep the GUI responsive. This limit can be changed
in the settings.
======================================================================================================================================================================
MemPro最简单的使用方法:
Insert these two lines into your code:
#define OVERRIDE_NEW_DELETE
#include "MemPro.cpp"
You will then be able to connect to your
app using MemPro.exe.
Once connected MemPro will track all
allocations and frees. At any time you can take a snapshot of the entire state
of memory. These snapshots can then be viewed in a variety of ways ( http://www.puredevsoftware.com/MemProUserGuide/SnapshotViews.htm
).
测试用例:
#include <iostream> #define OVERRIDE_NEW_DELETE
//#include "D:\ProgramFiles\PureDevSoftware\MemPro\MemProLib\src\MemPro.cpp"
#include "..\MemProLib\src\MemPro.cpp"//copy the MemPro src folder to your project using namespace std; float leakSize = 0.0;
int leakNum = ;
int* p = NULL; void main()
{
while()
{
p = new int();
leakNum++;
leakSize = leakNum*/1024.0f;
system("cls");
std::cout<<"leak size is: " << leakSize << "k";
}
}
编译通过,然后运行MemPro,在“Launch”按钮下填好该exe路径和工程路径(应该是需要工程路径来找pdb文件),点击“Launch”按钮即可;
其余的使用方式可以从ReadMe的内容中得到;
测试结果:
===============================================================================================================================================================
关于怎样将MemPro整合入UE4并且运用于多个平台的游戏:
- 原始的MemPro.cpp和MemPro.hpp可已从安装完MenPro后的安装目录找到:
-
/*
This software is provided 'as-is', without any express or implied warranty.
In no event will the author(s) be held liable for any damages arising from
the use of this software. Permission is granted to anyone to use this software for any purpose, including
commercial applications, and to alter it and redistribute it freely, subject to
the following restrictions: 1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution. Author: Stewart Lynch
www.puredevsoftware.com
slynch@puredevsoftware.com This code is released to the public domain, as explained at
http://creativecommons.org/publicdomain/zero/1.0/ MemProLib is the library that allows the MemPro application to communicate
with your application. ===========================================================
SETUP
=========================================================== * include MemPro.cpp and MemPro.hpp into your project. * Link with Dbghelp.lib and Ws2_32.lib - these are needed for the callstack trace and the network connection * Connect to your app with the MemPro
*/ //------------------------------------------------------------------------
// MemPro.hpp
//------------------------------------------------------------------------
/*
MemPro
Version: 1.3.7.0
*/
//------------------------------------------------------------------------
#ifndef MEMPRO_MEMPRO_H_INCLUDED
#define MEMPRO_MEMPRO_H_INCLUDED //------------------------------------------------------------------------
#define ENABLE_MEMPRO // **** enable/disable MemPro here! **** //------------------------------------------------------------------------
// macros for tracking allocs that define to nothing if disabled
#ifdef ENABLE_MEMPRO
#ifndef WAIT_FOR_CONNECT
#define WAIT_FOR_CONNECT false
#endif
#define MEMPRO_TRACK_ALLOC(p, size) MemPro::TrackAlloc(p, size, WAIT_FOR_CONNECT)
#define MEMPRO_TRACK_FREE(p) MemPro::TrackFree(p, WAIT_FOR_CONNECT)
#else
#define MEMPRO_TRACK_ALLOC(p, size) ((void)0)
#define MEMPRO_TRACK_FREE(p) ((void)0)
#endif //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
// Some platforms have problems initialising winsock from global constructors,
// to help get around this problem MemPro waits this amount of time before
// initialising. Allocs and freed that happen during this time are stored in
// a temporary buffer.
#define MEMPRO_INIT_DELAY 100 //------------------------------------------------------------------------
// MemPro waits this long before giving up on a connection after initialisation
#define MEMPRO_CONNECT_TIMEOUT 500 //------------------------------------------------------------------------
#include <stdlib.h> //------------------------------------------------------------------------
//#define WRITE_DUMP _T("allocs.mempro_dump") //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
enum PageState
{
Invalid = -,
Free,
Reserved,
Committed
}; //------------------------------------------------------------------------
enum PageType
{
page_Unknown = -,
page_Image,
page_Mapped,
page_Private
}; //------------------------------------------------------------------------ // You don't need to call this directly, it is automatically called on the first allocation.
// Only call this function if you want to be able to connect to your app before it has allocated any memory.
// If wait_for_connect is true this function will block until the external MemPro app has connected,
// this is useful to make sure that every single allocation is being tracked.
void Initialise(bool wait_for_connect=false); void Disconnect(); // kick all current connections, but can accept more void Shutdown(); // free all resources, no more connections allowed void TrackAlloc(void* p, size_t size, bool wait_for_connect=false); void TrackFree(void* p, bool wait_for_connect=false); bool IsPaused(); void SetPaused(bool paused); // this is used for the realtime memory graph.
void SendPageState(void* p, size_t size, PageState page_state, PageType page_type, unsigned int page_protection, bool send_memory); void TakeSnapshot(); // ignore these, for internal use only
void IncRef();
void DecRef();
} //------------------------------------------------------------------------
#ifndef WRITE_DUMP
namespace
{
// if we are using sockets we need to flush the sockets on global teardown
// This class is a trick to attempt to get mempro to shutdown after all other
// global objects.
class MemProGLobalScope
{
public:
MemProGLobalScope() { MemPro::IncRef(); }
~MemProGLobalScope() { MemPro::DecRef(); }
};
static MemProGLobalScope g_MemProGLobalScope;
}
#endif //------------------------------------------------------------------------
#ifdef OVERRIDE_NEW_DELETE #if defined(__APPLE__)
// if you get linker errors about duplicatly defined symbols please add a unexport.txt
// file to your build settings
// see here: https://developer.apple.com/library/mac/technotes/tn2185/_index.html
void* operator new(std::size_t size) throw(std::bad_alloc)
{
void* p = malloc(size);
MEMPRO_TRACK_ALLOC(p, size);
return p;
} void* operator new(std::size_t size, const std::nothrow_t&) throw()
{
void* p = malloc(size);
MEMPRO_TRACK_ALLOC(p, size);
return p;
} void operator delete(void* p) throw()
{
MEMPRO_TRACK_FREE(p);
free(p);
} void operator delete(void* p, const std::nothrow_t&) throw()
{
MEMPRO_TRACK_FREE(p);
free(p);
} void* operator new[](std::size_t size) throw(std::bad_alloc)
{
void* p = malloc(size);
MEMPRO_TRACK_ALLOC(p, size);
return p;
} void* operator new[](std::size_t size, const std::nothrow_t&) throw()
{
void* p = malloc(size);
MEMPRO_TRACK_ALLOC(p, size);
return p;
} void operator delete[](void* p) throw()
{
MEMPRO_TRACK_FREE(p);
free(p);
} void operator delete[](void* p, const std::nothrow_t&) throw()
{
MEMPRO_TRACK_FREE(p);
free(p);
}
#else
#include <malloc.h> void* operator new(size_t size)
{
void* p = malloc(size);
MEMPRO_TRACK_ALLOC(p, size);
return p;
} void operator delete(void* p)
{
MEMPRO_TRACK_FREE(p);
free(p);
} void* operator new[](size_t size)
{
void* p = malloc(size);
MEMPRO_TRACK_ALLOC(p, size);
return p;
} void operator delete[](void* p)
{
MEMPRO_TRACK_FREE(p);
free(p);
}
#endif #endif //------------------------------------------------------------------------
#ifdef OVERRIDE_MALLOC_FREE #if defined(_WIN32) || defined(_WIN64) || defined(WIN32) || defined(WIN64) || defined(__WIN32__) || defined(__WINDOWS__) // NOTE: for this to work, you will need to make sure you are linking STATICALLY to the crt. eg: /MTd __declspec(restrict) __declspec(noalias) void* malloc(size_t size)
{
void* p = HeapAlloc(GetProcessHeap(), , size);
MEMPRO_TRACK_ALLOC(p, size);
return p;
} __declspec(restrict) __declspec(noalias) void* realloc(void *p, size_t new_size)
{
MEMPRO_TRACK_FREE(p);
void* p_new = HeapReAlloc(GetProcessHeap(), , p, new_size);
MEMPRO_TRACK_ALLOC(p_new, new_size);
return p_new;
} __declspec(noalias) void free(void *p)
{
HeapFree(GetProcessHeap(), , p);
MEMPRO_TRACK_FREE(p);
}
#else
void *malloc(int size)
{
void* (*ptr)(int);
void* handle = (void*)-;
ptr = (void*)dlsym(handle, "malloc");
if(!ptr) abort();
void *p = (*ptr)(size);
MEMPRO_TRACK_ALLOC(p, size);
return p;
} void *realloc(void *p, int size)
{
MEMPRO_TRACK_FREE(p);
void * (*ptr)(void *, int);
void * handle = (void*) -;
ptr = (void*)dlsym(handle, "realloc");
if (!ptr) abort();
void* p_new = (*ptr)(p, size);
MEMPRO_TRACK_ALLOC(p_new, size);
return p_new;
} void free(void *p)
{
MEMPRO_TRACK_FREE(p);
void* (*ptr)(void*);
void* handle = (void*)-;
ptr = (void*)dlsym(handle, "free");
if (!ptr == NULL) abort();
(*ptr)(alloc);
}
#endif
#endif //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_MEMPRO_H_INCLUDEDMemPro.hpp
/*
This software is provided 'as-is', without any express or implied warranty.
In no event will the author(s) be held liable for any damages arising from
the use of this software. Permission is granted to anyone to use this software for any purpose, including
commercial applications, and to alter it and redistribute it freely, subject to
the following restrictions: 1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution. Author: Stewart Lynch
www.puredevsoftware.com
slynch@puredevsoftware.com This code is released to the public domain, as explained at
http://creativecommons.org/publicdomain/zero/1.0/ MemProLib is the library that allows the MemPro application to communicate
with your application.
*/ #if FRAMEPRO_TOOLSET_UE4
#include "CorePrivatePCH.h"
#endif #include "MemPro.hpp" //------------------------------------------------------------------------
// CallstackSet.cpp //------------------------------------------------------------------------
// MemProLib.hpp
#ifndef MEMPRO_MEMPROLIB_H_INCLUDED
#define MEMPRO_MEMPROLIB_H_INCLUDED //------------------------------------------------------------------------ //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
// **** The Target Platform **** // define ONE of these #if defined(_WIN32) || defined(_WIN64) || defined(WIN32) || defined(WIN64) || defined(__WIN32__) || defined(__WINDOWS__)
#if defined(_XBOX_ONE)
#define MEMPRO_PLATFORM_XBOXONE
#elif defined(_XBOX)
#define MEMPRO_PLATFORM_XBOX360
#else
#define MEMPRO_PLATFORM_WIN
#endif
#elif defined(__APPLE__)
#define MEMPRO_PLATFORM_APPLE
#else
#define MEMPRO_PLATFORM_UNIX
#endif //------------------------------------------------------------------------
#if defined(MEMPRO_PLATFORM_WIN) || defined(MEMPRO_PLATFORM_XBOX360) || defined(MEMPRO_PLATFORM_XBOXONE)
#define MEMPRO_WIN_BASED_PLATFORM
#endif //------------------------------------------------------------------------
#if defined(MEMPRO_PLATFORM_UNIX) || defined(MEMPRO_PLATFORM_APPLE)
#define MEMPRO_UNIX_BASED_PLATFORM
#endif //------------------------------------------------------------------------
#if defined(MEMPRO_PLATFORM_WIN)
#if defined(FRAMEPRO_TOOLSET_UE4) && FRAMEPRO_TOOLSET_UE4
#include "AllowWindowsPlatformTypes.h"
#endif
#ifndef WRITE_DUMP
#if defined(UNICODE) && !defined(_UNICODE)
#error for unicode builds please define both UNICODE and _UNICODE. See the FAQ for more details.
#endif
#if defined(AF_IPX) && !defined(_WINSOCK2API_)
#error winsock already defined. Please include winsock2.h before including windows.h or use WIN32_LEAN_AND_MEAN. See the FAQ for more info.
#endif
#define WIN32_LEAN_AND_MEAN #pragma warning(push)
#pragma warning(disable : 4668)
#include <winsock2.h>
#pragma warning(pop) #include <ws2tcpip.h>
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
#include <intrin.h>
#endif
#if defined(FRAMEPRO_TOOLSET_UE4) && FRAMEPRO_TOOLSET_UE4
#include "HideWindowsPlatformTypes.h"
#endif
#elif defined(MEMPRO_PLATFORM_XBOX360)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_PLATFORM_XBOXONE)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_UNIX_BASED_PLATFORM)
#include <execinfo.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#else
#error platform not defined
#endif //------------------------------------------------------------------------
#if defined(_WIN64) || defined(__LP64__) || defined(__x86_64__) || defined(__ppc64__)
#define MEMPRO64
#endif //------------------------------------------------------------------------
#if defined(MEMPRO_PLATFORM_XBOXONE) && !defined(MEMPRO64)
#error Please contact slynch@puredevsoftware.com for this platform
#endif //------------------------------------------------------------------------
#if defined(MEMPRO_WIN_BASED_PLATFORM)
#define MEMPRO_ASSERT(b) if(!(b)) DebugBreak()
#elif defined(MEMPRO_UNIX_BASED_PLATFORM)
#define MEMPRO_ASSERT(b) if(!(b)) __builtin_trap()
#else
#error // platform not defined
#endif //------------------------------------------------------------------------
#define MEMPRO_STATIC_ASSERT(expr) typedef char STATIC_ASSERT_TEST[ (expr) ] //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
class Allocator
{
public:
#if defined(MEMPRO_PLATFORM_XBOXONE)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_WIN_BASED_PLATFORM)
static void* Alloc(int size)
{
return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
}
static void Free(void* p, int size)
{
VirtualFree(p, size, MEM_RELEASE);
}
#else
static void* Alloc(int size) { return malloc(size); }
static void Free(void* p, int size) { free(p); }
#endif
}; //------------------------------------------------------------------------
#if defined(MEMPRO_WIN_BASED_PLATFORM)
typedef __int64 int64;
typedef unsigned __int64 uint64;
#elif defined(MEMPRO_UNIX_BASED_PLATFORM)
typedef long long int64;
typedef unsigned long long uint64;
#else
#error
#endif //------------------------------------------------------------------------
// platform specific stuff
#if defined(MEMPRO_WIN_BASED_PLATFORM)
#define MEMPRO_FORCEINLINE FORCEINLINE
#else
#define MEMPRO_FORCEINLINE inline
void memcpy_s(void* p_dst, int dst_len, void* p_src, int copy_len) { memcpy(p_dst, p_src, copy_len); }
void Sleep(int ms) { usleep( * ms); }
typedef int SOCKET;
typedef int DWORD;
enum SocketValues { INVALID_SOCKET = - };
#ifndef UINT_MAX
enum MaxValues { UINT_MAX = 0xffffffff };
#endif
void OutputDebugString(const char*) {}
#define _T(s) s
enum SocketErrorCodes { SOCKET_ERROR = - };
typedef sockaddr_in SOCKADDR_IN;
enum SystemDefines { MAX_PATH = };
#endif
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_MEMPROLIB_H_INCLUDED //------------------------------------------------------------------------
// CallstackSet.hpp
#ifndef MEMPRO_CALLSTACKSET_H_INCLUDED
#define MEMPRO_CALLSTACKSET_H_INCLUDED //------------------------------------------------------------------------ //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
struct Callstack
{
uint64* mp_Stack;
int m_ID;
int m_Size;
unsigned int m_Hash;
}; //------------------------------------------------------------------------
// A hash set collection for Callstack structures. Callstacks are added and
// retreived using the stack address array as the key.
// This class only allocates memory using virtual alloc/free to avoid going
// back into the mian allocator.
class CallstackSet
{
public:
CallstackSet(); ~CallstackSet(); Callstack* Get(uint64* p_stack, int stack_size, unsigned int hash); Callstack* Add(uint64* p_stack, int stack_size, unsigned int hash); void Clear(); private:
void Grow(); void Add(Callstack* p_callstack); //------------------------------------------------------------------------
// data
private:
Callstack** mp_Data;
unsigned int m_CapacityMask;
int m_Count;
int m_Capacity;
};
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_CALLSTACKSET_H_INCLUDED //------------------------------------------------------------------------
// BlockAllocator.hpp
#ifndef MEMPRO_MEMPRO_SPINLOCK_H_INCLUDED
#define MEMPRO_MEMPRO_SPINLOCK_H_INCLUDED //------------------------------------------------------------------------ //------------------------------------------------------------------------
// disable some warnings we are not interested in so that we can compile at warning level4
#ifdef MEMPRO_WIN_BASED_PLATFORM
#pragma warning(disable : 4100)
#endif //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
// a very simple allocator tat allocated blocks of 64k of memory using the
// templatized allocator.
template<class TAllocator>
class BlockAllocator
{
public:
inline BlockAllocator(); inline void* Alloc(int size); inline void Free(void* p); //------------------------------------------------------------------------
// data
private:
static const int m_BlockSize = *;
void* mp_CurBlock;
int m_CurBlockUsage;
}; //------------------------------------------------------------------------
template<class TAllocator>
BlockAllocator<TAllocator>::BlockAllocator()
: mp_CurBlock(NULL),
m_CurBlockUsage()
{
} //------------------------------------------------------------------------
template<class TAllocator>
void* BlockAllocator<TAllocator>::Alloc(int size)
{
MEMPRO_ASSERT(size < m_BlockSize); if(!mp_CurBlock || size > m_BlockSize - m_CurBlockUsage)
{
mp_CurBlock = TAllocator::Alloc(m_BlockSize);
MEMPRO_ASSERT(mp_CurBlock);
m_CurBlockUsage = ;
} void* p = (char*)mp_CurBlock + m_CurBlockUsage;
m_CurBlockUsage += size; return p;
} //------------------------------------------------------------------------
template<class TAllocator>
void BlockAllocator<TAllocator>::Free(void* p)
{
// do nothing
}
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_MEMPRO_SPINLOCK_H_INCLUDED //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
const int g_InitialCapacity = ; // must be a power of 2 MemPro::BlockAllocator<Allocator> g_BlockAllocator; //------------------------------------------------------------------------
inline bool StacksMatch(MemPro::Callstack* p_callstack, uint64* p_stack, int stack_size, unsigned int hash)
{
if(p_callstack->m_Size != stack_size)
return false; if(p_callstack->m_Hash != hash)
return false; for(int i=; i<stack_size; ++i)
if(p_callstack->mp_Stack[i] != p_stack[i])
return false; return true;
}
} //------------------------------------------------------------------------
MemPro::CallstackSet::CallstackSet()
: mp_Data((Callstack**)Allocator::Alloc(g_InitialCapacity*sizeof(Callstack*))),
m_CapacityMask(g_InitialCapacity-),
m_Count(),
m_Capacity(g_InitialCapacity)
{
memset(mp_Data, , g_InitialCapacity*sizeof(Callstack*));
} //------------------------------------------------------------------------
MemPro::CallstackSet::~CallstackSet()
{
Clear();
} //------------------------------------------------------------------------
void MemPro::CallstackSet::Grow()
{
int old_capacity = m_Capacity;
Callstack** p_old_data = mp_Data; // allocate a new set
m_Capacity *= ;
m_CapacityMask = m_Capacity - ;
int size = m_Capacity * sizeof(Callstack*);
mp_Data = (Callstack**)Allocator::Alloc(size);
memset(mp_Data, , size); // transfer callstacks from old set
m_Count = ;
for(int i=; i<old_capacity; ++i)
{
Callstack* p_callstack = p_old_data[i];
if(p_callstack)
Add(p_callstack);
} // release old buffer
Allocator::Free(p_old_data, old_capacity*sizeof(Callstack*));
} //------------------------------------------------------------------------
MemPro::Callstack* MemPro::CallstackSet::Get(uint64* p_stack, int stack_size, unsigned int hash)
{
int index = hash & m_CapacityMask; while(mp_Data[index] && !StacksMatch(mp_Data[index], p_stack, stack_size, hash))
index = (index + ) & m_CapacityMask; return mp_Data[index];
} //------------------------------------------------------------------------
MemPro::Callstack* MemPro::CallstackSet::Add(uint64* p_stack, int stack_size, unsigned int hash)
{
// grow the set if necessary
if(m_Count > m_Capacity/)
Grow(); // create a new callstack
Callstack* p_callstack = (Callstack*)g_BlockAllocator.Alloc(sizeof(Callstack));
p_callstack->m_ID = m_Count;
p_callstack->m_Size = stack_size;
p_callstack->mp_Stack = (uint64*)g_BlockAllocator.Alloc(stack_size*sizeof(uint64));
p_callstack->m_Hash = hash;
memcpy_s(p_callstack->mp_Stack, stack_size*sizeof(uint64), p_stack, stack_size*sizeof(uint64)); Add(p_callstack); return p_callstack;
} //------------------------------------------------------------------------
void MemPro::CallstackSet::Add(Callstack* p_callstack)
{
// find a clear index
int index = p_callstack->m_Hash & m_CapacityMask;
while(mp_Data[index])
index = (index + ) & m_CapacityMask; mp_Data[index] = p_callstack; ++m_Count;
} //------------------------------------------------------------------------
void MemPro::CallstackSet::Clear()
{
for(int i=; i<m_Capacity; ++i)
{
if(mp_Data[i])
g_BlockAllocator.Free(mp_Data[i]);
} Allocator::Free(mp_Data, m_Capacity*sizeof(Callstack*)); size_t size = g_InitialCapacity*sizeof(Callstack*);
mp_Data = (Callstack**)Allocator::Alloc((int)size);
memset(mp_Data, , size);
m_CapacityMask = g_InitialCapacity-;
m_Count = ;
m_Capacity = g_InitialCapacity;
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO
//------------------------------------------------------------------------
// MemPro.cpp //------------------------------------------------------------------------
// RingBuffer.hpp
#ifndef MEMPRO_RINGBUFFER_H_INCLUDED
#define MEMPRO_RINGBUFFER_H_INCLUDED //------------------------------------------------------------------------ //------------------------------------------------------------------------
// CriticalSection.hpp
#ifndef MEMPRO_CRITICALSECTION_H_INCLUDED
#define MEMPRO_CRITICALSECTION_H_INCLUDED //------------------------------------------------------------------------ //------------------------------------------------------------------------
#ifdef MEMPRO_UNIX_BASED_PLATFORM
#include <pthread.h>
#endif //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
class CriticalSection
{
public:
CriticalSection()
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
InitializeCriticalSection(&cs);
#else
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&cs, &attr);
#endif
} ~CriticalSection()
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
DeleteCriticalSection(&cs);
#else
pthread_mutex_destroy(&cs);
#endif
} void Enter()
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
EnterCriticalSection(&cs);
#else
pthread_mutex_lock(&cs);
#endif
} void Leave()
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
LeaveCriticalSection(&cs);
#else
pthread_mutex_unlock(&cs);
#endif
}
private: //------------------------------------------------------------------------
// data
private:
#ifdef MEMPRO_WIN_BASED_PLATFORM
CRITICAL_SECTION cs;
#else
pthread_mutex_t cs;
#endif
}; //------------------------------------------------------------------------
class CriticalSectionScope
{
public:
CriticalSectionScope(CriticalSection& in_cs) : cs(in_cs) { cs.Enter(); }
~CriticalSectionScope() { cs.Leave(); }
private:
CriticalSectionScope(const CriticalSectionScope&);
CriticalSectionScope& operator=(const CriticalSectionScope&);
CriticalSection& cs;
};
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_CRITICALSECTION_H_INCLUDED //------------------------------------------------------------------------
#ifdef MEMPRO_WIN_BASED_PLATFORM
// USE_INTRINSIC can also be enabled on 32bit platform, but I left it disabled because it doesn't work on XP
#ifdef MEMPRO64
#define USE_INTRINSIC
#endif
#endif #ifdef USE_INTRINSIC
#include <intrin.h>
#pragma intrinsic(_InterlockedCompareExchange64)
#pragma intrinsic(_InterlockedExchangeAdd64)
#endif //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------ //------------------------------------------------------------------------
// Event.hpp
#ifndef MEMPRO_EVENT_H_INCLUDED
#define MEMPRO_EVENT_H_INCLUDED //------------------------------------------------------------------------ //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
namespace MemPro
{
//--------------------------------------------------------------------
class Event
{
public:
//--------------------------------------------------------------------
Event(bool initial_state, bool auto_reset)
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
m_Handle = CreateEvent(NULL, !auto_reset, initial_state, NULL);
#else
pthread_cond_init(&m_Cond, NULL);
pthread_mutex_init(&m_Mutex, NULL);
m_Signalled = false;
m_AutoReset = auto_reset; if(initial_state)
Set();
#endif
} //--------------------------------------------------------------------
~Event()
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
CloseHandle(m_Handle);
#else
pthread_mutex_destroy(&m_Mutex);
pthread_cond_destroy(&m_Cond);
#endif
} //--------------------------------------------------------------------
void Set() const
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
SetEvent(m_Handle);
#else
pthread_mutex_lock(&m_Mutex);
m_Signalled = true;
pthread_mutex_unlock(&m_Mutex);
pthread_cond_signal(&m_Cond);
#endif
} //--------------------------------------------------------------------
void Reset()
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
ResetEvent(m_Handle);
#else
pthread_mutex_lock(&m_Mutex);
m_Signalled = false;
pthread_mutex_unlock(&m_Mutex);
#endif
} //--------------------------------------------------------------------
int Wait(int timeout=-) const
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
MEMPRO_STATIC_ASSERT(INFINITE == -);
return WaitForSingleObject(m_Handle, timeout) == /*WAIT_OBJECT_0*/;
#else
pthread_mutex_lock(&m_Mutex); if(m_Signalled)
{
m_Signalled = false;
pthread_mutex_unlock(&m_Mutex);
return true;
} if(timeout == -)
{
while(!m_Signalled)
pthread_cond_wait(&m_Cond, &m_Mutex); if(!m_AutoReset)
m_Signalled = false; pthread_mutex_unlock(&m_Mutex); return true;
}
else
{
timeval curr;
gettimeofday(&curr, NULL); timespec time;
time.tv_sec = curr.tv_sec + timeout / ;
time.tv_nsec = (curr.tv_usec * ) + ((timeout % ) * ); pthread_cond_timedwait(&m_Cond, &m_Mutex, &time); if(m_Signalled)
{
if(!m_AutoReset)
m_Signalled = false; pthread_mutex_unlock(&m_Mutex);
return true;
} pthread_mutex_unlock(&m_Mutex);
return false;
}
#endif
} //------------------------------------------------------------------------
// data
private:
#ifdef MEMPRO_WIN_BASED_PLATFORM
HANDLE m_Handle;
#else
mutable pthread_cond_t m_Cond;
mutable pthread_mutex_t m_Mutex;
mutable volatile bool m_Signalled;
bool m_AutoReset;
#endif
};
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_EVENT_H_INCLUDED //------------------------------------------------------------------------
//#define USE_CRITICAL_SECTIONS //------------------------------------------------------------------------
namespace MemPro
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
#ifndef USE_INTRINSIC
//------------------------------------------------------------------------
MEMPRO_FORCEINLINE int64 ssInterlockedCompareExchange64(int64 volatile *dest, int64 exchange, int64 comperand)
{
__asm
{
lea esi,comperand;
lea edi,exchange;
mov eax,[esi];
mov edx,[esi];
mov ebx,[edi];
mov ecx,[edi];
mov esi,dest;
lock CMPXCHG8B [esi];
}
} //------------------------------------------------------------------------
MEMPRO_FORCEINLINE int64 ssInterlockedExchangeAdd64(__inout int64 volatile *Addend, __in int64 Value)
{
int64 Old;
do
{
Old = *Addend;
} while (ssInterlockedCompareExchange64(Addend, Old + Value, Old) != Old);
return Old;
} //------------------------------------------------------------------------
#define _InterlockedCompareExchange64 ssInterlockedCompareExchange64
#define _InterlockedExchangeAdd64 ssInterlockedExchangeAdd64
#endif
#else
// no interlocked functions, so just use a critical section
CriticalSection g_CASCritSec;
MEMPRO_FORCEINLINE int64 _InterlockedCompareExchange64(int64 volatile *dest, int64 exchange, int64 comperand)
{
g_CASCritSec.Enter();
int64 old_value = *dest;
if(*dest == comperand)
*dest = exchange;
g_CASCritSec.Leave();
return old_value;
} MEMPRO_FORCEINLINE int64 _InterlockedExchangeAdd64(int64 volatile *Addend, int64 Value)
{
g_CASCritSec.Enter();
int64 old_value = *Addend;
*Addend += Value;
g_CASCritSec.Leave();
return old_value;
}
#endif //------------------------------------------------------------------------
// This ring buffer is a lockless buffer, designed to be accessed by no more
// than two threads, one thread adding to the buffer and one removing. The
// threads first request data and then add or remove tat data. The threads
// will sleep if there is no space to add or no data to remove. Once the
// threads have space on the buffer the data can be added or removed.
class RingBuffer
{
public:
//------------------------------------------------------------------------
struct Range
{
Range() {}
Range(void* p, int s) : mp_Buffer(p), m_Size(s) {} void* mp_Buffer;
int m_Size;
}; //------------------------------------------------------------------------
RingBuffer(char* p_buffer, int size)
: m_Size(size),
mp_Buffer(p_buffer),
m_UsedRange(),
m_BytesRemovedEvent(false, true),
m_BytesAddedEvent(false, true)
{
MEMPRO_ASSERT(IsPow2(size)); #ifdef MEMPRO_WIN_BASED_PLATFORM
MEMPRO_ASSERT((((int64)&m_UsedRange) & ) == );
#endif #ifdef USE_CRITICAL_SECTIONS
InitializeCriticalSection(&m_CriticalSection);
#endif
} //------------------------------------------------------------------------
inline bool IsPow2(int value)
{
return (value & (value-)) == ;
} //------------------------------------------------------------------------
int GetSize() const
{
return m_Size;
} //------------------------------------------------------------------------
void Lock() const
{
#ifdef USE_CRITICAL_SECTIONS
EnterCriticalSection(&m_CriticalSection);
#endif
} //------------------------------------------------------------------------
void Release() const
{
#ifdef USE_CRITICAL_SECTIONS
LeaveCriticalSection(&m_CriticalSection);
#endif
} //------------------------------------------------------------------------
int64 GetRangeAtomic() const
{
#ifdef USE_CRITICAL_SECTIONS
Lock();
int64 range = m_UsedRange;
Release();
#else
// there must be a better way to atomically read a 64 bit value.
int64 range = _InterlockedExchangeAdd64(const_cast<int64*>(&m_UsedRange), );
#endif
return range;
} //------------------------------------------------------------------------
// return the largest free range possible
Range GetFreeRange(int timeout=-) const
{
int64 range = GetRangeAtomic();
int size = (int)(range & 0xffffffff); // wait until there is some space
while(size == m_Size)
{
if(!m_BytesRemovedEvent.Wait(timeout))
return Range(NULL, ); range = GetRangeAtomic();
size = (int)(range & 0xffffffff);
} int start = (int)((range >> ) & 0xffffffff); // calculate the size
int free_start = (start + size) & (m_Size-);
int free_size = free_start < start ? start - free_start : m_Size - free_start; return Range(mp_Buffer + free_start, free_size);
} //------------------------------------------------------------------------
// return the largest used range
Range GetAllocatedRange(int timeout=-) const
{
int64 range = GetRangeAtomic();
#ifdef _XBOX
__lwsync(); // ensure that the allocated data has finished writing
#endif
int size = (int)(range & 0xffffffff); // wait until there is some data
while(!size)
{
if(!m_BytesAddedEvent.Wait(timeout))
return Range(NULL, ); range = GetRangeAtomic();
size = (int)(range & 0xffffffff);
} int start = (int)((range >> ) & 0xffffffff); // calculate the size
int max_size = m_Size - start;
if(size > max_size)
size = max_size; return Range(mp_Buffer + start, size);
} //------------------------------------------------------------------------
// tells the ring buffer how many bytes have been copied to the allocated range
void Add(int size)
{
Lock(); MEMPRO_ASSERT(size >= ); volatile int64 old_range;
int64 new_range; do
{
old_range = GetRangeAtomic(); int64 used_size = (old_range) & 0xffffffff;
used_size += size;
new_range = (old_range & 0xffffffff00000000LL) | used_size; } while(_InterlockedCompareExchange64(&m_UsedRange, new_range, old_range) != old_range); m_BytesAddedEvent.Set(); Release();
} //------------------------------------------------------------------------
// tells the ring buffer how many bytes have been removed from the allocated range
void Remove(int size)
{
Lock(); MEMPRO_ASSERT(size >= ); volatile int64 old_range;
int64 new_range;
int mask = m_Size - ; do
{
old_range = GetRangeAtomic(); int64 used_start = (old_range >> ) & 0xffffffff;
int64 used_size = (old_range) & 0xffffffff;
used_start = (used_start + size) & mask;
used_size -= size;
new_range = (used_start << ) | used_size; } while(_InterlockedCompareExchange64(&m_UsedRange, new_range, old_range) != old_range); m_BytesRemovedEvent.Set(); Release();
} //------------------------------------------------------------------------
int GetUsedBytes() const
{
return (int)(m_UsedRange & 0xffffffff);
} //------------------------------------------------------------------------
void Clear()
{
m_UsedRange = ;
m_BytesRemovedEvent.Reset();
m_BytesAddedEvent.Reset();
} //------------------------------------------------------------------------
// data
private:
int m_Size;
char* mp_Buffer; #ifdef MEMPRO_WIN_BASED_PLATFORM
// NOTE: this MUST be 64bit aligned
__declspec(align()) int64 m_UsedRange; // start index is the high int, size is the low int
#else
int64 m_UsedRange;
#endif #ifdef USE_CRITICAL_SECTIONS
mutable CRITICAL_SECTION m_CriticalSection;
#endif
Event m_BytesRemovedEvent;
Event m_BytesAddedEvent;
};
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_RINGBUFFER_H_INCLUDED //------------------------------------------------------------------------
// Packets.hpp
#ifndef MEMPRO_PACKETS_H_INCLUDED
#define MEMPRO_PACKETS_H_INCLUDED //------------------------------------------------------------------------ //------------------------------------------------------------------------
// MemProMisc.hpp
#ifndef MEMPRO_MEMPROMISC_H_INCLUDED
#define MEMPRO_MEMPROMISC_H_INCLUDED //------------------------------------------------------------------------ #include <stdlib.h> #ifdef MEMPRO_UNIX_BASED_PLATFORM
#include <byteswap.h>
#endif //------------------------------------------------------------------------
// disable some warnings we are not interested in so that we can compile at warning level4
#ifdef MEMPRO_WIN_BASED_PLATFORM
#pragma warning(disable : 4127)
#endif //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#define MEMPRO_SPINLOCK_FREE_VAL 0
#define MEMPRO_SPINLOCK_LOCKED_VAL 1
#define MEMPRO_YIELD_SPIN_COUNT 40
#define MEMPRO_SLEEP_SPIN_COUNT 200 //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
inline int Min(int a, int b) { return a < b ? a : b; } //------------------------------------------------------------------------
inline void SwapEndian(unsigned int& value)
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
value = _byteswap_ulong(value);
#else
value = __bswap_32(value);
#endif
} //------------------------------------------------------------------------
inline void SwapEndian(uint64& value)
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
value = _byteswap_uint64(value);
#else
value = __bswap_64(value);
#endif
} //------------------------------------------------------------------------
inline void SwapEndian(int64& value)
{
SwapEndian((uint64&)value);
} //------------------------------------------------------------------------
template<typename T>
inline void SwapEndian(T& value)
{
MEMPRO_ASSERT(sizeof(T) == sizeof(unsigned int));
SwapEndian((unsigned int&)value);
} //------------------------------------------------------------------------
inline void SwapEndianUInt64Array(void* p, int size)
{
MEMPRO_ASSERT(size % == );
uint64* p_uint64 = (uint64*)p;
uint64* p_end = p_uint64 + size/;
while(p_uint64 != p_end)
SwapEndian(*p_uint64++);
} //------------------------------------------------------------------------
// hi-res timer
#if defined(MEMPRO_PLATFORM_WIN)
inline uint64 GetRDTSC()
{
#ifdef MEMPRO64
return __rdtsc();
#else
__asm
{
; Flush the pipeline
XOR eax, eax
CPUID
; Get RDTSC counter in edx:eax
RDTSC
}
#endif
}
#define GET_CLOCK_COUNT(time) time = GetRDTSC();
#elif defined(MEMPRO_PLATFORM_XBOX360)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_PLATFORM_XBOXONE)
#error Please contact slynch@puredevsoftware.com for this platform
#endif //------------------------------------------------------------------------
inline int64 GetTime()
{
int64 time; #ifdef MEMPRO_WIN_BASED_PLATFORM
GET_CLOCK_COUNT(time);
#else
timeval curr;
gettimeofday(&curr, NULL);
time = ((int64)curr.tv_sec) * + curr.tv_usec;
#endif
return time;
} //------------------------------------------------------------------------
inline int64 GetTickFrequency()
{
Sleep();
int64 start = GetTime();
Sleep();
int64 end = GetTime();
return end - start;
} //------------------------------------------------------------------------
inline void SetThreadName(unsigned int thread_id, const char* p_name)
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
// see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
const unsigned int MS_VC_EXCEPTION=0x406D1388; struct THREADNAME_INFO
{
unsigned int dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
unsigned int dwThreadID; // Thread ID (-1=caller thread).
unsigned int dwFlags; // Reserved for future use, must be zero.
}; // on the xbox setting thread names messes up the XDK COM API that UnrealConsole uses so check to see if they have been
// explicitly enabled
Sleep();
THREADNAME_INFO ThreadNameInfo;
ThreadNameInfo.dwType = 0x1000;
ThreadNameInfo.szName = p_name;
ThreadNameInfo.dwThreadID = thread_id;
ThreadNameInfo.dwFlags = ; __try
{
RaiseException( MS_VC_EXCEPTION, , sizeof(ThreadNameInfo)/sizeof(ULONG_PTR), (ULONG_PTR*)&ThreadNameInfo );
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
#else
// not supported
#endif
} //------------------------------------------------------------------------
inline void SmallFastMemCpy(void* p_dst, void* p_src, int size)
{
MEMPRO_ASSERT((((size_t)p_dst) & ) == );
MEMPRO_ASSERT((((size_t)p_src) & ) == );
MEMPRO_ASSERT((size & ) == ); unsigned int uint_count = size / sizeof(unsigned int);
unsigned int* p_uint_dst = (unsigned int*)p_dst;
unsigned int* p_uint_src = (unsigned int*)p_src;
for(unsigned int i=; i<uint_count; ++i)
*p_uint_dst++ = *p_uint_src++;
}
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_MEMPROMISC_H_INCLUDED //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
// This file contains all of te packets that can be sent to the MemPro app. //------------------------------------------------------------------------
enum PacketType
{
EInvalid = 0xabcd,
EAllocPacket,
EFreePacket,
ECallstackPacket,
EPageStatePacket,
EPageStateStartPacket, // for backwards compatibility
EPageStateEndPacket_OLD,
EVirtualMemStats,
ETakeSnapshot,
EVMemStats,
EPageStateEndPacket,
EDataStoreEndPacket,
EPulsePacket,
ERequestShutdown
}; //------------------------------------------------------------------------
enum MemProVersion
{
Version =
}; //------------------------------------------------------------------------
enum MemProClientFlags
{
SendPageData = ,
SendPageDataWithMemory,
EShutdownComplete
}; //------------------------------------------------------------------------
// value that is sent immediatley after connection to detect big endian
enum EEndianKey
{
EndianKey = 0xabcdef01
}; //------------------------------------------------------------------------
enum Platform
{
Platform_Windows,
Platform_Unix
}; //------------------------------------------------------------------------
struct PacketHeader
{
PacketType m_PacketType;
int m_Padding;
int64 m_Time; void SwapEndian()
{
MemPro::SwapEndian(m_PacketType);
MemPro::SwapEndian(m_Time);
}
}; //------------------------------------------------------------------------
struct ConnectPacket
{
uint64 m_Padding; // for backwards compatibility int64 m_ConnectTime;
int64 m_TickFrequency; int m_Version;
int m_PtrSize; Platform m_Platform;
int m_Padding2; void SwapEndian()
{
MemPro::SwapEndian(m_Version);
MemPro::SwapEndian(m_ConnectTime);
MemPro::SwapEndian(m_TickFrequency);
MemPro::SwapEndian(m_PtrSize);
}
}; //------------------------------------------------------------------------
struct AllocPacket
{
uint64 m_Addr;
uint64 m_Size;
int m_CallstackID;
int m_Padding; void SwapEndian()
{
MemPro::SwapEndian(m_Addr);
MemPro::SwapEndian(m_Size);
MemPro::SwapEndian(m_CallstackID);
}
}; //------------------------------------------------------------------------
struct FreePacket
{
uint64 m_Addr; void SwapEndian()
{
MemPro::SwapEndian(m_Addr);
}
}; //------------------------------------------------------------------------
struct PageStatePacket
{
uint64 m_Addr;
uint64 m_Size;
PageState m_State;
PageType m_Type;
unsigned int m_Protection;
int m_SendingMemory; void SwapEndian()
{
MemPro::SwapEndian(m_Addr);
MemPro::SwapEndian(m_Size);
MemPro::SwapEndian(m_State);
MemPro::SwapEndian(m_Type);
MemPro::SwapEndian(m_Protection);
MemPro::SwapEndian(m_SendingMemory);
}
}; //------------------------------------------------------------------------
struct VirtualMemStatsPacket
{
uint64 m_Reserved;
uint64 m_Committed; void SwapEndian()
{
MemPro::SwapEndian(m_Reserved);
MemPro::SwapEndian(m_Committed);
}
}; //------------------------------------------------------------------------
struct IgnoreMemRangePacket
{
uint64 m_Addr;
uint64 m_Size; void SwapEndian()
{
MemPro::SwapEndian(m_Addr);
MemPro::SwapEndian(m_Size);
}
};
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_PACKETS_H_INCLUDED //------------------------------------------------------------------------
// Socket.hpp
#ifndef MEMPRO_SOCKET_H_INCLUDED
#define MEMPRO_SOCKET_H_INCLUDED //------------------------------------------------------------------------ //------------------------------------------------------------------------
#if defined(ENABLE_MEMPRO) && !defined(WRITE_DUMP) //------------------------------------------------------------------------
#ifdef MEMPRO_WIN_BASED_PLATFORM
#pragma warning(push)
#pragma warning(disable : 4100)
#endif //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
class SocketImp; //------------------------------------------------------------------------
class Socket
{
public:
inline Socket(); inline ~Socket(); void Disconnect(); bool Bind(const char* p_port); bool StartListening(); bool Accept(Socket& client_socket); int Receive(void* p_buffer, int size); bool Send(void* p_buffer, int size); inline bool IsValid() const { return m_Socket != INVALID_SOCKET; } private:
bool InitialiseWSA(); void CleanupWSA(); void HandleError(); //------------------------------------------------------------------------
// data
SOCKET m_Socket;
}; //------------------------------------------------------------------------
Socket::Socket()
: m_Socket(INVALID_SOCKET)
{
} //------------------------------------------------------------------------
Socket::~Socket()
{
CleanupWSA();
}
} //------------------------------------------------------------------------
#ifdef MEMPRO_WIN_BASED_PLATFORM
#pragma warning(pop)
#endif //------------------------------------------------------------------------
#endif // #if defined(ENABLE_MEMPRO) && !defined(WRITE_DUMP) //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_SOCKET_H_INCLUDED //------------------------------------------------------------------------
// Thread.hpp
#ifndef MEMPRO_THREAD_H_INCLUDED
#define MEMPRO_THREAD_H_INCLUDED //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#ifdef MEMPRO_WIN_BASED_PLATFORM
#pragma warning(push)
#pragma warning(disable : 4100)
#endif //------------------------------------------------------------------------
#ifndef MEMPRO_WIN_BASED_PLATFORM
#include <pthread.h>
#endif //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
typedef int (*ThreadMain)(void*); //------------------------------------------------------------------------
class Thread
{
public:
Thread(); void CreateThread(ThreadMain p_thread_main, void* p_param=NULL); bool IsAlive() const { return m_Alive; } private:
#ifdef MEMPRO_WIN_BASED_PLATFORM
static unsigned long WINAPI PlatformThreadMain(void* p_param);
#else
static void* PlatformThreadMain(void* p_param);
#endif //------------------------------------------------------------------------
// data
private:
#ifdef MEMPRO_WIN_BASED_PLATFORM
mutable HANDLE m_Handle;
#else
mutable pthread_t m_Thread;
#endif
mutable bool m_Alive; mutable ThreadMain mp_ThreadMain;
mutable void* mp_Param;
};
} //------------------------------------------------------------------------
#ifdef MEMPRO_WIN_BASED_PLATFORM
#pragma warning(pop)
#endif //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_THREAD_H_INCLUDED #include <new>
#include <stdio.h>
#include <time.h>
#include <limits.h> #if defined(MEMPRO_PLATFORM_WIN) && !((!defined(MIDL_PASS) && defined(_M_IX86) && !defined(_M_CEE_PURE)) || defined(MemoryBarrier))
#include <atomic>
#endif #ifdef MEMPRO_WIN_BASED_PLATFORM
#include <tchar.h>
#endif //------------------------------------------------------------------------
// disable some warnings we are not interested in so that we can compile at warning level4
#ifdef MEMPRO_WIN_BASED_PLATFORM
#pragma warning(disable : 4127)
#pragma warning(disable : 4100)
#endif //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#if !defined(WRITE_DUMP) && defined(MEMPRO_WIN_BASED_PLATFORM)
#pragma comment(lib, "Ws2_32.lib")
#endif //------------------------------------------------------------------------
// if you are having problems compiling this on your platform undefine ENUMERATE_ALL_MODULES and it send info for just the main module
#ifndef MEMPRO_PLATFORM_XBOXONE
#define ENUMERATE_ALL_MODULES
#endif #ifdef ENUMERATE_ALL_MODULES
#ifdef MEMPRO_WIN_BASED_PLATFORM #if FRAMEPRO_TOOLSET_UE4
#include "AllowWindowsPlatformTypes.h"
#endif #pragma warning(push)
#pragma warning(disable : 4091)
#include <Dbghelp.h>
#pragma warning(pop) #pragma comment(lib, "Dbghelp.lib") #if FRAMEPRO_TOOLSET_UE4
#include "HideWindowsPlatformTypes.h"
#endif
#else
#include <link.h>
#endif
#endif //------------------------------------------------------------------------
#ifdef VMEM_STATS
namespace VMem { void SendStatsToMemPro(void (*send_fn)(void*, int, void*), void* p_context); }
#endif //------------------------------------------------------------------------
//#define TEST_ENDIAN //#define PACKET_START_END_MARKERS #ifdef TEST_ENDIAN
#define ENDIAN_TEST(a) a
#else
#define ENDIAN_TEST(a)
#endif //------------------------------------------------------------------------
// if both of these options are commented out it will use CaptureStackBackTrace (or backtrace on linux)
//#define USE_STACKWALK64 // much slower but possibly more reliable. USE_STACKWALK64 only implemented for x86 builds.
//#define USE_RTLVIRTUALUNWIND // reported to be faster than StackWalk64 - only available on x64 builds
//#define USE_RTLCAPTURESTACKBACKTRACE // system version of USE_RTLVIRTUALUNWIND - only available on x64 builds #if FRAMEPRO_TOOLSET_UE4
#define USE_RTLCAPTURESTACKBACKTRACE
#endif //------------------------------------------------------------------------
#ifdef MEMPRO_WIN_BASED_PLATFORM
#define THREAD_LOCAL_STORAGE __declspec(thread)
#else
#define THREAD_LOCAL_STORAGE __thread
#endif //------------------------------------------------------------------------
namespace MemPro
{
THREAD_LOCAL_STORAGE void** g_CallstackDataTLS = NULL;
} //------------------------------------------------------------------------
#ifdef MEMPRO_PLATFORM_XBOXONE
#error Please contact slynch@puredevsoftware.com for this platform
#endif //------------------------------------------------------------------------
#ifdef USE_RTLCAPTURESTACKBACKTRACE #ifndef MEMPRO64
#error USE_RTLVIRTUALUNWIND only available on x64 builds. Please use a different stack walk function.
#endif //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
void RTLCaptureStackBackTrace(void** stack, int max_stack_size, unsigned int& hash, int& stack_size)
{
memset(stack, , max_stack_size* sizeof(void*));
stack_size = ::RtlCaptureStackBackTrace(,max_stack_size-, stack, (PDWORD)&hash);
stack[stack_size] = ;
}
} #endif // #ifdef USE_RTLCAPTURESTACKBACKTRACE //------------------------------------------------------------------------
#ifdef USE_RTLVIRTUALUNWIND #ifndef MEMPRO64
#error USE_RTLVIRTUALUNWIND only available on x64 builds. Please use a different stack walk function.
#endif namespace MemPro
{
//------------------------------------------------------------------------
__declspec(noinline) VOID VirtualUnwindStackWalk(void** stack, int max_stack_size)
{
CONTEXT context;
memset(&context, , sizeof(context));
RtlCaptureContext(&context); UNWIND_HISTORY_TABLE unwind_history_table;
RtlZeroMemory(&unwind_history_table, sizeof(UNWIND_HISTORY_TABLE)); int frame = ;
for (; frame < max_stack_size-; ++frame)
{
stack[frame] = (void*)context.Rip; ULONG64 image_base;
PRUNTIME_FUNCTION runtime_function = RtlLookupFunctionEntry(context.Rip, &image_base, &unwind_history_table); if (!runtime_function)
{
// If we don't have a RUNTIME_FUNCTION, then we've encountered
// a leaf function. Adjust the stack appropriately.
context.Rip = (ULONG64)(*(PULONG64)context.Rsp);
context.Rsp += ;
}
else
{
// Otherwise, call upon RtlVirtualUnwind to execute the unwind for us.
KNONVOLATILE_CONTEXT_POINTERS nv_context;
RtlZeroMemory(&nv_context, sizeof(KNONVOLATILE_CONTEXT_POINTERS)); PVOID handler_data;
ULONG64 establisher_frame; RtlVirtualUnwind(
/*UNW_FLAG_NHANDLER*/,
image_base,
context.Rip,
runtime_function,
&context,
&handler_data,
&establisher_frame,
&nv_context);
} // If we reach an RIP of zero, this means that we've walked off the end
// of the call stack and are done.
if (!context.Rip)
break;
} stack[frame] = ;
}
}
#endif //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
int g_MemProRefs = ; //------------------------------------------------------------------------
const int PAGE_SIZE = ; //------------------------------------------------------------------------
void InitialiseInternal(); //------------------------------------------------------------------------
// MemPro will initialise on the first allocation, but this global ensures
// that MemPro is initialised in the main module. This is sometimes necessary
// if the first allocation comes from a dll.
/*
class Initialiser
{
public:
Initialiser() { MemPro::Initialise(WAIT_FOR_CONNECT); }
} g_Initialiser;
*/ //------------------------------------------------------------------------
// port number
#if defined(MEMPRO_PLATFORM_WIN)
const char* g_DefaultPort = "";
#elif defined(MEMPRO_PLATFORM_XBOX360)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_PLATFORM_XBOXONE)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_UNIX_BASED_PLATFORM)
const char* g_DefaultPort = "";
#else
#error platform not defined
#endif //------------------------------------------------------------------------
#if defined(MEMPRO_PLATFORM_WIN)
#if (NTDDI_VERSION > NTDDI_WINXP)
#define STACK_TRACE_SIZE 128
#else
#define STACK_TRACE_SIZE 62
#endif
#elif defined(MEMPRO_PLATFORM_XBOX360)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_PLATFORM_XBOXONE)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_UNIX_BASED_PLATFORM)
#define STACK_TRACE_SIZE 128
#else
#error platform not defined
#endif //------------------------------------------------------------------------
// globals
const int g_RingBufferSize = *; #ifdef MEMPRO64
uint64 g_MaxAddr = ULLONG_MAX;
#else
uint64 g_MaxAddr = UINT_MAX;
#endif //------------------------------------------------------------------------
#ifdef WRITE_DUMP
FILE* gp_DumpFile = NULL;
#endif //------------------------------------------------------------------------
uint64 ToUInt64(void* p)
{
#ifdef MEMPRO64
return (uint64)p;
#else
unsigned int u = (unsigned int)p; // cast to uint first to avoid signed bit in casting
return (uint64)u;
#endif
} //------------------------------------------------------------------------
struct DataStorePageHeader
{
int m_Size;
DataStorePageHeader* mp_Next;
}; //------------------------------------------------------------------------
struct CallstackCapture
{
void** mp_Stack;
int m_Size;
unsigned int m_Hash;
}; //------------------------------------------------------------------------
void BaseAddressLookupFunction()
{
} //------------------------------------------------------------------------
class CMemPro
{
public:
CMemPro(); bool Initialise(); void Shutdown(); void Disconnect(bool listen_for_new_connection); void TrackAlloc(void* p, size_t size, bool wait_for_connect); void TrackFree(void* p, bool wait_for_connect); void SendPageState(void* p, size_t size, PageState page_state, PageType page_type, unsigned int page_protection, bool send_memory); void TakeSnapshot(); int SendThreadMain(void* p_param); #ifndef WRITE_DUMP
int ReceiveThreadMain(void* p_param);
#endif int WaitForConnectionThreadMain(void* p_param); void Lock() { m_CriticalSection.Enter(); } void Release() { m_CriticalSection.Leave(); } void WaitForConnectionOnInitialise(); bool IsPaused(); void SetPaused(bool paused); private:
static void GetStackTrace(void** stack, int& stack_size, unsigned int& hash); void SendModuleInfo(); void SendExtraModuleInfo(int64 ModuleBase); void SendString(const char* p_str); #ifdef ENUMERATE_ALL_MODULES
#ifdef MEMPRO_WIN_BASED_PLATFORM
#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
static BOOL CALLBACK EnumerateLoadedModulesCallback(__in PCSTR ModuleName,__in DWORD64 ModuleBase,__in ULONG ModuleSize,__in_opt PVOID UserContext);
#else
static BOOL CALLBACK EnumerateLoadedModulesCallback(__in PCSTR ModuleName,__in ULONG ModuleBase, __in ULONG ModuleSize,__in_opt PVOID UserContext);
#endif
#else
int static EnumerateLoadedModulesCallback(struct dl_phdr_info* info, size_t size, void* data);
#endif
#endif
void StoreData(const void* p_data, int size); void BlockUntilSendThreadEmpty(); void SendStoredData(); void ClearStoreData(); inline bool SendThreadStillAlive() const; void FlushRingBufferForShutdown(); void SendData(const void* p_data, int size); bool SocketSendData(const void* p_data, int size); static void StaticSendVMemStatsData(void* p_data, int size, void* p_context); void SendVMemStatsData(void* p_data, int size); void SendData(unsigned int value); inline void SendPacketHeader(PacketType value); void SendStartMarker(); void SendEndMarker(); inline void Send(bool value); template<typename T> void Send(T& value) { SendData(&value, sizeof(value)); } void Send(unsigned int value) { SendData(value); } void SendPageState(bool send_memory); void SendVMemStats(); void SendVirtualMemStats(); void** AllocateStackTraceData(); CallstackCapture CaptureCallstack(); int SendCallstack(const CallstackCapture& callstack_capture); bool WaitForConnection(); bool WaitForConnectionIfListening(); static int SendThreadMainStatic(void* p_param); static int ReceiveThreadMainStatic(void* p_param); static int WaitForConnectionThreadMainStatic(void* p_param); static int PulseThreadMainStatic(void* p_param); void PulseThreadMain(); void BlockUntilReadyToSend(); //------------------------------------------------------------------------
// data
#ifndef WRITE_DUMP
Socket m_ListenSocket;
Socket m_ClientSocket;
#endif CallstackSet m_CallstackSet; RingBuffer m_RingBuffer;
char m_RingBufferMem[g_RingBufferSize]; volatile bool m_Connected;
volatile bool m_ReadyToSend; volatile bool m_InEvent; volatile bool m_Paused; Event m_StartedListeningEvent;
Event m_WaitForConnectThreadFinishedEvent;
Event m_SendThreadFinishedEvent;
Event m_ReceiveThreadFinishedEvent;
Event m_MemProReadyToShutdownEvent;
Event m_PulseThreadFinished; volatile bool m_StartedListening;
volatile bool m_InitialConnectionTimedOut; int m_LastPageStateSend;
int m_PageStateInterval; int m_LastVMemStatsSend;
int m_VMemStatsSendInterval; bool m_WaitForConnect; static const int m_DataStorePageSize = ;
DataStorePageHeader* mp_DataStoreHead; // used to store allocs before initialised
DataStorePageHeader* mp_DataStoreTail; Thread m_SendThread;
Thread m_ReceiveThread;
Thread m_PulseThread;
Thread m_WaitForConnectionThread; bool m_FlushedRingBufferForShutdown; CriticalSection m_CriticalSection;
CriticalSection m_DisconnectCriticalSection; int m_ModulesSent; volatile bool m_ShuttingDown; BlockAllocator<Allocator> m_BlockAllocator;
}; //------------------------------------------------------------------------
char g_MemProMem[sizeof(CMemPro)];
CMemPro* gp_MemPro = NULL;
volatile bool g_ShuttingDown = false; //------------------------------------------------------------------------
inline CMemPro* GetMemPro()
{
if(!gp_MemPro)
InitialiseInternal(); return gp_MemPro;
} //------------------------------------------------------------------------
CMemPro::CMemPro()
: m_RingBuffer(m_RingBufferMem, g_RingBufferSize),
m_Connected(false),
m_ReadyToSend(false),
m_InEvent(false),
m_Paused(false),
m_StartedListeningEvent(false, false),
m_WaitForConnectThreadFinishedEvent(false, false),
m_SendThreadFinishedEvent(true, false),
m_ReceiveThreadFinishedEvent(true, false),
m_MemProReadyToShutdownEvent(false, false),
m_PulseThreadFinished(true, false),
m_StartedListening(false),
m_InitialConnectionTimedOut(false),
m_LastPageStateSend(),
m_PageStateInterval(),
m_LastVMemStatsSend(),
m_VMemStatsSendInterval(),
m_WaitForConnect(false),
mp_DataStoreHead(NULL),
mp_DataStoreTail(NULL),
m_FlushedRingBufferForShutdown(false),
m_ModulesSent(),
m_ShuttingDown(false)
{
} //------------------------------------------------------------------------
inline unsigned int GetHash(void** p_stack, int stack_size)
{
#ifdef MEMPRO64
const unsigned int prime = 0x01000193;
unsigned int hash = prime;
void** p = p_stack;
for(int i=; i<stack_size; ++i)
{
uint64 key = ToUInt64(*p++);
key = (~key) + (key << );
key = key ^ (key >> );
key = key * ;
key = key ^ (key >> );
key = key + (key << );
key = key ^ (key >> );
hash = hash ^ (unsigned int)key;
} return hash;
#else
const unsigned int prime = 0x01000193;
unsigned int hash = prime;
for(int i=; i<stack_size; ++i)
hash = (hash * prime) ^ (unsigned int)p_stack[i]; return hash;
#endif
} //------------------------------------------------------------------------
inline unsigned int GetHashAndStackSize(void** p_stack, int& stack_size)
{
#ifdef MEMPRO64
const unsigned int prime = 0x01000193;
unsigned int hash = prime;
stack_size = ;
void** p = p_stack;
while(*p)
{
uint64 key = ToUInt64(*p++);
key = (~key) + (key << );
key = key ^ (key >> );
key = key * ;
key = key ^ (key >> );
key = key + (key << );
key = key ^ (key >> );
hash = hash ^ (unsigned int)key;
++stack_size;
} return hash;
#else
const unsigned int prime = 0x01000193;
unsigned int hash = prime;
stack_size = ;
while(p_stack[stack_size])
{
hash = (hash * prime) ^ (unsigned int)p_stack[stack_size];
++stack_size;
} return hash;
#endif
} //------------------------------------------------------------------------
void CMemPro::GetStackTrace(void** stack, int& stack_size, unsigned int& hash)
{
#if defined(MEMPRO_PLATFORM_WIN)
#if defined(USE_STACKWALK64) #ifdef MEMPRO64
#error USE_STACKWALK64 only works in x86 builds. Please use a different stack walk funtion.
#endif // get the context
CONTEXT context;
memset(&context, , sizeof(context));
RtlCaptureContext(&context); // setup the stack frame
STACKFRAME64 stack_frame;
memset(&stack_frame, , sizeof(stack_frame));
stack_frame.AddrPC.Mode = AddrModeFlat;
stack_frame.AddrFrame.Mode = AddrModeFlat;
stack_frame.AddrStack.Mode = AddrModeFlat;
#ifdef MEMPRO64
DWORD machine = IMAGE_FILE_MACHINE_IA64;
stack_frame.AddrPC.Offset = context.Rip;
stack_frame.AddrFrame.Offset = context.Rsp;
stack_frame.AddrStack.Offset = context.Rbp;
#else
DWORD machine = IMAGE_FILE_MACHINE_I386;
stack_frame.AddrPC.Offset = context.Eip;
stack_frame.AddrFrame.Offset = context.Ebp;
stack_frame.AddrStack.Offset = context.Esp;
#endif
HANDLE thread = GetCurrentThread(); static HANDLE process = GetCurrentProcess(); stack_size = ;
while(StackWalk64(
machine,
process,
thread,
&stack_frame,
&context,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL) && stack_size < STACK_TRACE_SIZE)
{
void* p = (void*)(stack_frame.AddrPC.Offset);
stack[stack_size++] = p;
}
hash = GetHash(stack, stack_size);
#elif defined(USE_RTLVIRTUALUNWIND)
MemPro::VirtualUnwindStackWalk(stack, STACK_TRACE_SIZE);
hash = GetHashAndStackSize(stack, stack_size);
#elif defined(USE_RTLCAPTURESTACKBACKTRACE)
MemPro::RTLCaptureStackBackTrace(stack, STACK_TRACE_SIZE, hash, stack_size);
#else
CaptureStackBackTrace(, STACK_TRACE_SIZE, stack, (PDWORD)&hash);
for(stack_size = ; stack_size<STACK_TRACE_SIZE; ++stack_size)
if(!stack[stack_size])
break;
#endif
#elif defined(MEMPRO_PLATFORM_XBOX360)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_PLATFORM_XBOXONE)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_UNIX_BASED_PLATFORM)
stack_size = backtrace(stack, STACK_TRACE_SIZE);
hash = GetHashAndStackSize(stack, stack_size);
#else
#error platform not defined
#endif
} //------------------------------------------------------------------------
void CMemPro::StaticSendVMemStatsData(void* p_data, int size, void* p_context)
{
CMemPro* p_this = (CMemPro*)p_context;
p_this->SendVMemStatsData(p_data, size);
} //------------------------------------------------------------------------
void CMemPro::SendVMemStatsData(void* p_data, int size)
{
static char buffer[];
MEMPRO_ASSERT(size <= (int)sizeof(buffer));
memcpy_s(buffer, sizeof(buffer), p_data, size);
ENDIAN_TEST(SwapEndianUInt64Array(buffer, size));
SendData(buffer, size);
} //------------------------------------------------------------------------
void CMemPro::StoreData(const void* p_data, int size)
{
MEMPRO_ASSERT(size < m_DataStorePageSize - (int)sizeof(DataStorePageHeader)); if(!mp_DataStoreTail || mp_DataStoreTail->m_Size + size > m_DataStorePageSize)
{
DataStorePageHeader* p_new_page = (DataStorePageHeader*)Allocator::Alloc(m_DataStorePageSize);
p_new_page->m_Size = sizeof(DataStorePageHeader);
p_new_page->mp_Next = NULL; if(mp_DataStoreTail)
mp_DataStoreTail->mp_Next = p_new_page;
else
mp_DataStoreHead = p_new_page; mp_DataStoreTail = p_new_page;
} memcpy((char*)mp_DataStoreTail + mp_DataStoreTail->m_Size, p_data, size);
mp_DataStoreTail->m_Size += size;
} //------------------------------------------------------------------------
void CMemPro::BlockUntilSendThreadEmpty()
{
// wait for the send thread to have sent all of the stored data
while(m_Connected && m_RingBuffer.GetAllocatedRange().m_Size)
Sleep();
} //------------------------------------------------------------------------
void CMemPro::SendStoredData()
{
if(!m_Connected)
return; DataStorePageHeader* p_page = mp_DataStoreHead; if(p_page)
{
while(p_page)
{
DataStorePageHeader* p_next = p_page->mp_Next; SendData((char*)p_page + sizeof(DataStorePageHeader), p_page->m_Size - sizeof(DataStorePageHeader));
Allocator::Free(p_page, m_DataStorePageSize); p_page = p_next;
} SendPacketHeader(EDataStoreEndPacket);
SendEndMarker();
} #ifndef WRITE_DUMP
BlockUntilSendThreadEmpty();
#endif mp_DataStoreHead = mp_DataStoreTail = NULL;
} //------------------------------------------------------------------------
void CMemPro::ClearStoreData()
{
DataStorePageHeader* p_page = mp_DataStoreHead;
while(p_page)
{
DataStorePageHeader* p_next = p_page->mp_Next;
Allocator::Free(p_page, m_DataStorePageSize);
p_page = p_next;
} mp_DataStoreHead = mp_DataStoreTail = NULL; m_CallstackSet.Clear();
} //------------------------------------------------------------------------
void CMemPro::Send(bool value)
{
unsigned int uint_value = value ? : ;
Send(uint_value);
} //------------------------------------------------------------------------
bool CMemPro::SendThreadStillAlive() const
{
return m_SendThread.IsAlive();
} //------------------------------------------------------------------------
void CMemPro::FlushRingBufferForShutdown()
{
if(m_FlushedRingBufferForShutdown)
return;
m_FlushedRingBufferForShutdown = true; RingBuffer::Range range = m_RingBuffer.GetAllocatedRange();
while(range.m_Size)
{
SocketSendData(range.mp_Buffer, range.m_Size);
range = m_RingBuffer.GetAllocatedRange();
}
} //------------------------------------------------------------------------
void CMemPro::SendData(const void* p_data, int size)
{
MEMPRO_ASSERT((size & ) == ); if(!m_Connected)
{
StoreData(p_data, size);
return;
} if(!SendThreadStillAlive())
{
FlushRingBufferForShutdown();
SocketSendData(p_data, size);
}
else
{
int bytes_to_copy = size;
char* p_src = (char*)p_data;
while(bytes_to_copy)
{
RingBuffer::Range range;
do {
range = m_RingBuffer.GetFreeRange();
if(!m_Connected)
return;
} while(!range.m_Size);
if(!m_Connected)
return; int copy_size = Min(range.m_Size, bytes_to_copy);
SmallFastMemCpy(range.mp_Buffer, p_src, copy_size);
p_src += copy_size;
bytes_to_copy -= copy_size; m_RingBuffer.Add(copy_size);
}
}
} //------------------------------------------------------------------------
// slightly more optimal version for sending a single uint. Because all ringbuffer
// operations are 4 byte aligned we can be guaranteed that the uint won't be split
// between the end and start of the buffer, we will always get it in one piece.
void CMemPro::SendData(unsigned int value)
{
if(!m_Connected)
{
StoreData(&value, sizeof(value));
return;
} if(!SendThreadStillAlive())
{
FlushRingBufferForShutdown();
SocketSendData(&value, sizeof(value));
#ifdef WRITE_DUMP
fflush(gp_DumpFile);
#endif
}
else
{
RingBuffer::Range range;
do {
range = m_RingBuffer.GetFreeRange();
if(!m_Connected)
return;
} while(!range.m_Size);
if(!m_Connected)
return; MEMPRO_ASSERT(range.m_Size >= (int)sizeof(unsigned int));
MEMPRO_ASSERT((((size_t)range.mp_Buffer) & ) == );
*(unsigned int*)range.mp_Buffer = value; m_RingBuffer.Add(sizeof(value));
}
} //------------------------------------------------------------------------
void CMemPro::SendPacketHeader(PacketType value)
{
SendStartMarker(); PacketHeader header;
header.m_PacketType = value;
header.m_Time = GetTime(); Send(header);
} //------------------------------------------------------------------------
void CMemPro::SendStartMarker()
{
#ifdef PACKET_START_END_MARKERS
unsigned int start_marker = 0xabcdef01;
ENDIAN_TEST(SwapEndian(start_marker));
Send(start_marker);
#endif
} //------------------------------------------------------------------------
void CMemPro::SendEndMarker()
{
#ifdef PACKET_START_END_MARKERS
unsigned int end_marker = 0xaabbccdd;
ENDIAN_TEST(SwapEndian(end_marker));
Send(end_marker);
#endif
} //------------------------------------------------------------------------
void CMemPro::SendPageState(bool send_memory)
{
CriticalSectionScope lock(m_CriticalSection); SendPacketHeader(EPageStateStartPacket);
SendEndMarker(); #ifdef MEMPRO_WIN_BASED_PLATFORM
MEMORY_BASIC_INFORMATION info;
memset(&info, , sizeof(info)); uint64 addr = ; HANDLE process = GetCurrentProcess(); bool found_page = false; while(addr < g_MaxAddr)
{
uint64 last_addr = addr; if(VirtualQueryEx(process, (void*)addr, &info, sizeof(info)) != )
{
if((info.State == MEM_RESERVE || info.State == MEM_COMMIT) && info.Protect != PAGE_NOACCESS)
{
PageState page_state;
switch(info.State)
{
case MEM_RESERVE: page_state = MemPro::Reserved; break;
case MEM_COMMIT: page_state = MemPro::Committed; break;
default: page_state = MemPro::Committed; MEMPRO_ASSERT(false); break;
} #if defined(MEMPRO_PLATFORM_WIN)
PageType page_type;
switch(info.Type)
{
case MEM_IMAGE: page_type = page_Image; break;
case MEM_MAPPED: page_type = page_Mapped; break;
case MEM_PRIVATE: page_type = page_Private; break;
default: page_type = page_Unknown; break;
}
#elif defined(MEMPRO_PLATFORM_XBOX360)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_PLATFORM_XBOXONE)
#error Please contact slynch@puredevsoftware.com for this platform
#else
#error platform not defined
#endif
SendPageState(info.BaseAddress, info.RegionSize, page_state, page_type, info.Protect, send_memory);
} addr += info.RegionSize;
found_page = true;
}
else
{
if(!found_page)
addr += PAGE_SIZE;
else
break; // VirtualQueryEx should only fail when it gets to the end, assuming it has found at least one page
} if(addr < last_addr) // handle wrap around
break;
}
#endif
SendPacketHeader(EPageStateEndPacket); IgnoreMemRangePacket range_packet;
range_packet.m_Addr = ToUInt64(m_RingBufferMem);
range_packet.m_Size = sizeof(m_RingBufferMem);
Send(range_packet); SendEndMarker();
} //------------------------------------------------------------------------
void CMemPro::SendVMemStats()
{
#ifdef VMEM_STATS
Send(EVMemStats); int64 time = GetTime();
Send(time); VMem::SendStatsToMemPro(StaticSendVMemStatsData, this);
#endif
} //------------------------------------------------------------------------
void CMemPro::SendVirtualMemStats()
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
MEMORY_BASIC_INFORMATION info;
memset(&info, , sizeof(info)); uint64 addr = ;
size_t reserved = ;
size_t committed = ; HANDLE process = GetCurrentProcess(); bool started = false; while(addr < g_MaxAddr)
{
uint64 last_addr = addr; if(VirtualQueryEx(process, (void*)addr, &info, sizeof(info)) != )
{
switch(info.State)
{
case MEM_RESERVE: reserved += info.RegionSize; break;
case MEM_COMMIT: committed += info.RegionSize; break;
} addr += info.RegionSize; started = true;
}
else
{
if(started)
break; addr = (addr & (~((size_t)PAGE_SIZE-))) + PAGE_SIZE;
} if(addr < last_addr) // handle wrap around
break;
} #ifdef MEMPRO_PLATFORM_XBOX360
#error Please contact slynch@puredevsoftware.com for this platform
#endif
reserved += committed; SendPacketHeader(EVirtualMemStats); VirtualMemStatsPacket packet;
packet.m_Reserved = reserved;
packet.m_Committed = committed;
ENDIAN_TEST(packet.SwapEndian());
Send(packet);
#else
SendPacketHeader(EVirtualMemStats); VirtualMemStatsPacket packet;
packet.m_Reserved = ;
packet.m_Committed = ;
ENDIAN_TEST(packet.SwapEndian());
Send(packet);
#endif
SendEndMarker();
} //------------------------------------------------------------------------
void** CMemPro::AllocateStackTraceData()
{
CriticalSectionScope lock(m_CriticalSection);
return (void**)m_BlockAllocator.Alloc(STACK_TRACE_SIZE*sizeof(void*));
} //------------------------------------------------------------------------
CallstackCapture CMemPro::CaptureCallstack()
{
CallstackCapture callstack; callstack.mp_Stack = g_CallstackDataTLS;
if(!callstack.mp_Stack)
{
callstack.mp_Stack = AllocateStackTraceData();
g_CallstackDataTLS = callstack.mp_Stack;
} callstack.m_Hash = ;
callstack.m_Size = ;
GetStackTrace(callstack.mp_Stack, callstack.m_Size, callstack.m_Hash); #ifdef USE_RTLVIRTUALUNWIND
const int ignore_count = ;
#else
const int ignore_count = ;
#endif
callstack.m_Size -= ignore_count;
if(callstack.m_Size <= )
{
callstack.mp_Stack[] = (void*)-;
callstack.m_Size = ;
} return callstack;
} //------------------------------------------------------------------------
int CMemPro::SendCallstack(const CallstackCapture& callstack_capture)
{
void** p_stack = callstack_capture.mp_Stack;
int stack_size = callstack_capture.m_Size;
int hash = callstack_capture.m_Hash; #ifdef MEMPRO64
uint64* stack64 = (uint64*)p_stack;
#else
static uint64 stack64_static[STACK_TRACE_SIZE];
for(int i=; i<stack_size; ++i)
stack64_static[i] = ToUInt64(p_stack[i]);
uint64* stack64 = stack64_static;
#endif Callstack* p_callstack = m_CallstackSet.Get(stack64, stack_size, hash); if(!p_callstack)
{
p_callstack = m_CallstackSet.Add(stack64, stack_size, hash); SendPacketHeader(ECallstackPacket); int callstack_id = p_callstack->m_ID;
#ifdef TEST_ENDIAN
SwapEndian(callstack_id);
#endif
Send(callstack_id); int send_stack_size = stack_size;
#ifdef TEST_ENDIAN
for(int i=; i<stack_size; ++i) SwapEndian(stack64[i]);
SwapEndian(send_stack_size);
#endif
Send(send_stack_size);
SendData(stack64, stack_size*sizeof(uint64)); SendEndMarker();
} return p_callstack->m_ID;
} //------------------------------------------------------------------------
void CMemPro::TakeSnapshot()
{
CriticalSectionScope lock(m_CriticalSection);
SendPacketHeader(ETakeSnapshot);
} //------------------------------------------------------------------------
int CMemPro::SendThreadMainStatic(void* p_param)
{
return gp_MemPro->SendThreadMain(p_param);
} //------------------------------------------------------------------------
bool CMemPro::SocketSendData(const void* p_data, int size)
{
#ifdef WRITE_DUMP
MEMPRO_ASSERT(gp_DumpFile);
size_t result = fwrite(p_data, size, , gp_DumpFile);
MEMPRO_ASSERT(result == );
return true;
#else
return m_ClientSocket.Send((void*)p_data, size);
#endif
} //------------------------------------------------------------------------
int CMemPro::SendThreadMain(void* p_param)
{
while(m_Connected)
{
RingBuffer::Range range;
do {
range = m_RingBuffer.GetAllocatedRange(); // timeout: check for disconnect every 100 ms
if(!m_Connected)
{
m_SendThreadFinishedEvent.Set();
return ;
}
} while(!range.m_Size); if(!SocketSendData(range.mp_Buffer, range.m_Size))
{
m_SendThreadFinishedEvent.Set();
Disconnect(true);
return ;
} m_RingBuffer.Remove(range.m_Size);
} m_SendThreadFinishedEvent.Set();
return ;
} //------------------------------------------------------------------------
#ifndef WRITE_DUMP
int CMemPro::ReceiveThreadMainStatic(void* p_param)
{
return gp_MemPro->ReceiveThreadMain(p_param);
}
#endif //------------------------------------------------------------------------
#ifndef WRITE_DUMP
int CMemPro::ReceiveThreadMain(void* p_param)
{
while(m_Connected)
{
unsigned int flag = ; if(m_ClientSocket.Receive(&flag, sizeof(flag)) != sizeof(flag))
{
m_ReceiveThreadFinishedEvent.Set();
Disconnect(true);
return ;
} switch(flag)
{
case SendPageData: SendPageState(false/*send_memory*/); break;
case SendPageDataWithMemory: SendPageState(true/*send memory*/); break;
case EShutdownComplete: m_MemProReadyToShutdownEvent.Set(); break;
}
} m_ReceiveThreadFinishedEvent.Set();
return ;
}
#endif //------------------------------------------------------------------------
// http://www.debuginfo.com/articles/debuginfomatch.html #ifdef MEMPRO_WIN_BASED_PLATFORM
struct CV_HEADER
{
int Signature;
int Offset;
}; struct CV_INFO_PDB20
{
CV_HEADER CvHeader;
int Signature;
int Age;
char PdbFileName[MAX_PATH];
}; struct CV_INFO_PDB70
{
int CvSignature;
GUID Signature;
int Age;
char PdbFileName[MAX_PATH];
};
#endif void CMemPro::SendExtraModuleInfo(int64 ModuleBase)
{
#ifdef MEMPRO_PLATFORM_WIN
IMAGE_DOS_HEADER* p_dos_header = (IMAGE_DOS_HEADER*)ModuleBase;
IMAGE_NT_HEADERS* p_nt_header = (IMAGE_NT_HEADERS*)((char*)ModuleBase + p_dos_header->e_lfanew);
IMAGE_OPTIONAL_HEADER& optional_header = p_nt_header->OptionalHeader;
IMAGE_DATA_DIRECTORY& image_data_directory = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
IMAGE_DEBUG_DIRECTORY* p_debug_info_array = (IMAGE_DEBUG_DIRECTORY*)(ModuleBase + image_data_directory.VirtualAddress);
int count = image_data_directory.Size / sizeof(IMAGE_DEBUG_DIRECTORY);
for(int i=; i<count; ++i)
{
if(p_debug_info_array[i].Type == IMAGE_DEBUG_TYPE_CODEVIEW)
{
char* p_cv_data = (char*)(ModuleBase + p_debug_info_array[i].AddressOfRawData);
if(strncmp(p_cv_data, "RSDS", ) == )
{
CV_INFO_PDB70* p_cv_info = (CV_INFO_PDB70*)p_cv_data;
Send(true); // sending info
Send(p_cv_info->Age);
Send(p_cv_info->Signature);
SendString(p_cv_info->PdbFileName);
return; // returning here
}
else if(strncmp(p_cv_data, "NB10", ) == )
{
Send(true); // sending info
CV_INFO_PDB20* p_cv_info = (CV_INFO_PDB20*)p_cv_data;
Send(p_cv_info->Age);
Send(p_cv_info->Signature);
SendString(p_cv_info->PdbFileName);
return; // returning here
}
}
}
#endif
// failed to find info
Send(false); // not sending info
} //------------------------------------------------------------------------
void CMemPro::SendString(const char* p_str)
{
const int max_path_len = ;
int len = (int)strlen(p_str) + ;
MEMPRO_ASSERT(len <= max_path_len); // round up to 4 bytes
static char temp[max_path_len];
memset(temp, , sizeof(temp));
memcpy(temp, p_str, len); int rounded_len = ((int)len + ) & ~;
Send(rounded_len); SendData(temp, rounded_len);
} //------------------------------------------------------------------------
#ifdef ENUMERATE_ALL_MODULES
#ifdef MEMPRO_WIN_BASED_PLATFORM
#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
// depending on your platform you may need to change PCSTR to PSTR for ModuleName
BOOL CALLBACK CMemPro::EnumerateLoadedModulesCallback(__in PCSTR ModuleName,__in DWORD64 ModuleBase,__in ULONG ModuleSize,__in_opt PVOID UserContext)
#else
BOOL CALLBACK CMemPro::EnumerateLoadedModulesCallback(__in PCSTR ModuleName,__in ULONG ModuleBase,__in ULONG ModuleSize,__in_opt PVOID UserContext)
#endif
{
CMemPro* p_this = (CMemPro*)UserContext; int64 module_base = ModuleBase;
p_this->Send(module_base); p_this->SendString(ModuleName); p_this->SendExtraModuleInfo(ModuleBase); ++p_this->m_ModulesSent; return true;
}
#else
int CMemPro::EnumerateLoadedModulesCallback(struct dl_phdr_info* info, size_t size, void* data)
{
CMemPro* p_this = (CMemPro*)data; int64 module_base = ;
for (int j = ; j < info->dlpi_phnum; j++)
{
if (info->dlpi_phdr[j].p_type == PT_LOAD)
{
module_base = info->dlpi_addr + info->dlpi_phdr[j].p_vaddr;
break;
}
} if(p_this->m_ModulesSent == )
{
// send the module base address
int64 lookup_fn_marker = 0xabcdefabcdef1LL;
p_this->Send(lookup_fn_marker); int64 module_base = (int64)BaseAddressLookupFunction; // use the address of the BaseAddressLookupFunction function so that we can work it out later
p_this->Send(module_base); // get the module name
char arg1[];
char char_filename[MAX_PATH];
sprintf(arg1, "/proc/%d/exe", getpid());
memset(char_filename, , MAX_PATH);
readlink(arg1, char_filename, MAX_PATH-);
p_this->SendString(char_filename);
}
else
{
p_this->Send(module_base);
p_this->SendString(info->dlpi_name);
} p_this->SendExtraModuleInfo(); ++p_this->m_ModulesSent; return ;
}
#endif
#endif //------------------------------------------------------------------------
void CMemPro::SendModuleInfo()
{
Send(true); // indicate we are going to be sending module signatures - for backwards compatibility
uint64 extra_module_info = 0xabcdef;
Send(extra_module_info); m_ModulesSent = ; // if you are having problems compiling this on your platform undefine ENUMERATE_ALL_MODULES and it send info for just the main module
#ifdef ENUMERATE_ALL_MODULES
#ifdef MEMPRO_WIN_BASED_PLATFORM
#ifdef MEMPRO64
EnumerateLoadedModules64(GetCurrentProcess(), EnumerateLoadedModulesCallback, this);
#else
EnumerateLoadedModules(GetCurrentProcess(), EnumerateLoadedModulesCallback, this);
#endif
#else
dl_iterate_phdr(EnumerateLoadedModulesCallback, this);
#endif
#endif // if ENUMERATE_ALL_MODULES is disabled or enumeration failed for some reason, fall back
// to getting the base address for the main module. This will always for for all platforms.
if(m_ModulesSent == )
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
static int module = ;
HMODULE module_handle = ;
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)&module, &module_handle); int64 module_base = (int64)module_handle;
Send(module_base); TCHAR tchar_filename[MAX_PATH] = { };
GetModuleFileName(NULL, tchar_filename, MAX_PATH); char char_filename[MAX_PATH]; #ifdef UNICODE
size_t chars_converted = ;
wcstombs_s(&chars_converted, char_filename, tchar_filename, MAX_PATH);
#else
strcpy_s(char_filename, tchar_filename);
#endif SendString(char_filename); Send(false); // not sending SendExtraModuleInfo
#else
// let MemPro know we are sending the lookup function address, not the base address
uint64 use_module_base_addr_marker = 0xabcdefabcdef1LL;
Send(use_module_base_addr_marker); // send the module base address
int64 module_base = (int64)BaseAddressLookupFunction; // use the address of the BaseAddressLookupFunction function so that we can work it out later
Send(module_base); // send the module name
char char_filename[MAX_PATH]; // get the module name
char arg1[];
sprintf(arg1, "/proc/%d/exe", getpid());
memset(char_filename, , MAX_PATH);
readlink(arg1, char_filename, MAX_PATH-); SendString(char_filename); Send(false); // not sending SendExtraModuleInfo
#endif
} uint64 terminator = ;
Send(terminator);
} //------------------------------------------------------------------------
bool CMemPro::WaitForConnection()
{
m_CriticalSection.Enter(); #ifdef WRITE_DUMP
OutputDebugString(_T("MemPro writing to dump file " WRITE_DUMP _T("\n")));
#ifdef MEMPRO_WIN_BASED_PLATFORM
_tfopen_s(&gp_DumpFile, WRITE_DUMP, _T("wb"));
#else
gp_DumpFile = fopen(WRITE_DUMP, _T("wb"));
#endif
MEMPRO_ASSERT(gp_DumpFile); m_Connected = true; m_SendThreadFinishedEvent.Reset(); // start the sending thread
int thread_id = ;
m_SendThread.CreateThread(SendThreadMainStatic, &thread_id);
SetThreadName(thread_id, "MemPro write thread");
#else
// start listening for connections
if(m_ListenSocket.IsValid() && !m_ListenSocket.StartListening())
{
m_WaitForConnectThreadFinishedEvent.Set(); // do this before Shutdown
Shutdown();
m_CriticalSection.Leave();
return false;
} m_StartedListening = true;
m_StartedListeningEvent.Set(); // Accept a client socket
bool accepted = false;
if(m_ListenSocket.IsValid())
{
m_CriticalSection.Leave();
accepted = m_ListenSocket.Accept(m_ClientSocket); if(!accepted)
{
bool shutting_down = m_ShuttingDown;
m_WaitForConnectThreadFinishedEvent.Set(); // do this before Shutdown
if(!shutting_down) // check shutting down here in case CMemPro has been destructed
{
m_CriticalSection.Enter();
Shutdown();
m_CriticalSection.Leave();
}
return false;
}
} m_CriticalSection.Enter(); m_Connected = true; m_SendThreadFinishedEvent.Reset();
m_ReceiveThreadFinishedEvent.Reset(); // start the sending thread
int send_thread_id = ;
m_SendThread.CreateThread(SendThreadMainStatic, &send_thread_id);
SetThreadName(send_thread_id, "MemPro send thread"); // start the receiving thread
int receive_thread_id = ;
m_ReceiveThread.CreateThread(ReceiveThreadMainStatic, &receive_thread_id);
SetThreadName(receive_thread_id, "MemPro receive thread");
#endif
// send the connect key
unsigned int endian_key = (unsigned int)EndianKey;
ENDIAN_TEST(SwapEndian(endian_key));
Send(endian_key); // send the connect packet
ConnectPacket connect_packet;
connect_packet.m_Padding = 0xabcdabcd;
connect_packet.m_Version = MemPro::Version;
connect_packet.m_TickFrequency = GetTickFrequency();
connect_packet.m_ConnectTime = GetTime(); connect_packet.m_PtrSize = sizeof(void*); #ifdef MEMPRO_WIN_BASED_PLATFORM
connect_packet.m_Platform = Platform_Windows;
#else
connect_packet.m_Platform = Platform_Unix;
#endif ENDIAN_TEST(connect_packet.SwapEndian());
Send(connect_packet); SendModuleInfo(); #if defined(MEMPRO_PLATFORM_WIN)
#if (!defined(MIDL_PASS) && defined(_M_IX86) && !defined(_M_CEE_PURE)) || defined(MemoryBarrier)
MemoryBarrier();
#else
std::atomic_thread_fence(std::memory_order_seq_cst);
#endif
#elif defined(MEMPRO_PLATFORM_XBOX360)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_PLATFORM_XBOXONE)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_UNIX_BASED_PLATFORM)
__sync_synchronize();
#else
#error platform not defined
#endif SendStoredData(); m_ReadyToSend = true; m_WaitForConnectThreadFinishedEvent.Set();
m_CriticalSection.Leave(); // start the pulse thread
int pulse_thread_id = ;
m_PulseThreadFinished.Reset();
m_PulseThread.CreateThread(PulseThreadMainStatic, &pulse_thread_id);
SetThreadName(pulse_thread_id, "MemPro pulse thread"); return true;
} //------------------------------------------------------------------------
int CMemPro::WaitForConnectionThreadMainStatic(void* p_param)
{
return gp_MemPro->WaitForConnectionThreadMain(p_param);
} //------------------------------------------------------------------------
int CMemPro::PulseThreadMainStatic(void* p_param)
{
gp_MemPro->PulseThreadMain();
return ;
} //------------------------------------------------------------------------
void CMemPro::PulseThreadMain()
{
while(m_Connected)
{
{
CriticalSectionScope lock(m_CriticalSection);
if(!m_Connected)
break; SendPacketHeader(EPulsePacket);
SendEndMarker();
} Sleep();
} m_PulseThreadFinished.Set();
} //------------------------------------------------------------------------
int CMemPro::WaitForConnectionThreadMain(void* p_param)
{
#ifdef WRITE_DUMP
Sleep(MEMPRO_INIT_DELAY);
#else
if(!m_ListenSocket.IsValid())
{
Sleep(MEMPRO_INIT_DELAY); bool bind_result = m_ListenSocket.Bind(g_DefaultPort); if(!bind_result)
OutputDebugString(_T("MemPro ERROR: Failed to bind port. This usually means that another process is already running with MemPro enabled.\n"));
MEMPRO_ASSERT(bind_result);
if(!bind_result)
return ;
}
#endif
WaitForConnection(); return ;
} //------------------------------------------------------------------------
bool CMemPro::Initialise()
{
m_WaitForConnectionThread.CreateThread(WaitForConnectionThreadMainStatic, NULL); return true;
} //------------------------------------------------------------------------
void CMemPro::Shutdown()
{
m_ShuttingDown = true; // wait for MemPro to have handled all data
if(m_SendThread.IsAlive())
{
SendPacketHeader(ERequestShutdown);
SendEndMarker();
m_MemProReadyToShutdownEvent.Wait( * ); // do this so that we don't start listening after the listen socket has been shutdown and deadlock
m_CriticalSection.Leave();
m_StartedListeningEvent.Wait();
m_CriticalSection.Enter(); if(m_WaitForConnect)
{
BlockUntilReadyToSend();
BlockUntilSendThreadEmpty();
}
} Disconnect(false/*listen_for_new_connection*/); m_CriticalSection.Leave();
m_PulseThreadFinished.Wait();
m_CriticalSection.Enter(); #ifndef WRITE_DUMP
m_ListenSocket.Disconnect(); if(m_WaitForConnectionThread.IsAlive())
m_WaitForConnectThreadFinishedEvent.Wait(); #ifdef MEMPRO_WIN_BASED_PLATFORM
WSACleanup();
#endif
#endif
} //------------------------------------------------------------------------
void CMemPro::Disconnect(bool listen_for_new_connection)
{
CriticalSectionScope lock(m_DisconnectCriticalSection); if(m_Connected)
{
m_ReadyToSend = false;
m_Connected = false; // wait for the send thread to shutdown
m_SendThreadFinishedEvent.Wait();
m_SendThreadFinishedEvent.Reset(); #ifdef WRITE_DUMP
fclose(gp_DumpFile);
gp_DumpFile = NULL;
#else
// close the client socket
m_ClientSocket.Disconnect(); // wait for the receive thread to shutdown
m_ReceiveThreadFinishedEvent.Wait();
m_ReceiveThreadFinishedEvent.Reset();
#endif
// clear stuff
m_CallstackSet.Clear(); m_RingBuffer.Clear(); #ifndef WRITE_DUMP
if(listen_for_new_connection)
{
CriticalSectionScope lock2(m_CriticalSection); // start listening for another connection
m_ListenSocket.Disconnect();
m_StartedListeningEvent.Reset();
m_StartedListening = false;
m_InitialConnectionTimedOut = false;
m_WaitForConnectionThread.CreateThread(WaitForConnectionThreadMainStatic, NULL);
}
#endif
}
} //------------------------------------------------------------------------
void CMemPro::BlockUntilReadyToSend()
{
#ifndef WRITE_DUMP
if(m_ListenSocket.IsValid())
{
OutputDebugString(_T("Waiting for connection to MemPro...\n")); int64 start_time = GetTime();
while(!m_ReadyToSend && m_ListenSocket.IsValid() &&
(m_WaitForConnect || ((GetTime() - start_time) / (double)GetTickFrequency()) * < MEMPRO_CONNECT_TIMEOUT))
{
m_CriticalSection.Leave();
Sleep();
m_CriticalSection.Enter();
} if(m_ReadyToSend)
{
OutputDebugString(_T("Connected to MemPro!\n"));
}
else
{
m_InitialConnectionTimedOut = true;
ClearStoreData();
OutputDebugString(_T("Failed to connect to MemPro\n"));
}
}
#endif
} //------------------------------------------------------------------------
// return true to continue processing event (either connected or before started listening)
bool CMemPro::WaitForConnectionIfListening()
{
#ifdef WRITE_DUMP
return true;
#else
if(!m_ReadyToSend && !m_InitialConnectionTimedOut)
{
CriticalSectionScope lock(m_CriticalSection); // store data until we have started listening
if(!m_StartedListening)
return true; BlockUntilReadyToSend();
} return m_ReadyToSend;
#endif
} //------------------------------------------------------------------------
void CMemPro::TrackAlloc(void* p, size_t size, bool wait_for_connect)
{
if(m_Paused)
return; m_WaitForConnect = wait_for_connect; if(!WaitForConnectionIfListening())
return; CallstackCapture callstack_capture = CaptureCallstack(); CriticalSectionScope lock(m_CriticalSection); #ifndef WRITE_DUMP
#ifdef MEMPRO_WIN_BASED_PLATFORM
if(m_ListenSocket.IsValid())
{
int now = GetTickCount();
if(now - m_LastPageStateSend > m_PageStateInterval)
{
SendVirtualMemStats();
m_LastPageStateSend = now;
} if(now - m_LastVMemStatsSend > m_VMemStatsSendInterval)
{
SendVMemStats();
m_LastVMemStatsSend = now;
}
}
#endif
#endif
if(m_InEvent)
return;
m_InEvent = true; int callstack_id = SendCallstack(callstack_capture); SendPacketHeader(EAllocPacket); AllocPacket packet;
packet.m_Addr = ToUInt64(p);
packet.m_Size = size;
packet.m_CallstackID = callstack_id;
packet.m_Padding = 0xef12ef12;
ENDIAN_TEST(packet.SwapEndian());
Send(packet); SendEndMarker(); m_InEvent = false;
} //------------------------------------------------------------------------
void CMemPro::TrackFree(void* p, bool wait_for_connect)
{
if(m_Paused)
return; m_WaitForConnect = wait_for_connect; if(!WaitForConnectionIfListening())
return; CriticalSectionScope lock(m_CriticalSection); if(m_InEvent)
return;
m_InEvent = true; SendPacketHeader(EFreePacket); FreePacket packet;
packet.m_Addr = ToUInt64(p);
ENDIAN_TEST(packet.SwapEndian());
Send(packet); SendEndMarker(); m_InEvent = false;
} //------------------------------------------------------------------------
bool CMemPro::IsPaused()
{
return m_Paused;
} //------------------------------------------------------------------------
void CMemPro::SetPaused(bool paused)
{
m_Paused = paused;
} //------------------------------------------------------------------------
void CMemPro::SendPageState(void* p, size_t size, PageState page_state, PageType page_type, unsigned int page_protection, bool send_memory)
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
if(!WaitForConnectionIfListening())
return; SendPacketHeader(EPageStatePacket); bool send_page_mem = send_memory && page_state == Committed && (page_protection & (PAGE_NOACCESS | PAGE_EXECUTE | PAGE_GUARD)) == ; PageStatePacket packet;
packet.m_Addr = ToUInt64(p);
packet.m_Size = size;
packet.m_State = page_state;
packet.m_Type = page_type;
packet.m_Protection = page_protection;
packet.m_SendingMemory = send_page_mem;
ENDIAN_TEST(packet.SwapEndian());
Send(packet); if(send_page_mem)
{
MEMPRO_ASSERT(!(size % PAGE_SIZE));
char* p_page = (char*)p;
char* p_end_page = p_page + size;
while(p_page != p_end_page)
{
SendData(p_page, PAGE_SIZE);
p_page += PAGE_SIZE;
}
} SendEndMarker();
#endif
} //------------------------------------------------------------------------
void CMemPro::WaitForConnectionOnInitialise()
{
m_WaitForConnect = true; m_StartedListeningEvent.Wait(); CriticalSectionScope lock(m_CriticalSection);
BlockUntilReadyToSend();
}
} //------------------------------------------------------------------------
void MemPro::InitialiseInternal()
{
if(!gp_MemPro && !g_ShuttingDown)
{
gp_MemPro = (CMemPro*)g_MemProMem;
new (gp_MemPro)CMemPro();
gp_MemPro->Initialise();
}
} //------------------------------------------------------------------------
void MemPro::IncRef()
{
++g_MemProRefs;
} //------------------------------------------------------------------------
void MemPro::DecRef()
{
if(--g_MemProRefs == )
Shutdown();
} //------------------------------------------------------------------------
// called by the APP (not internally)
void MemPro::Initialise(bool wait_for_connect)
{
InitialiseInternal(); if(wait_for_connect)
gp_MemPro->WaitForConnectionOnInitialise();
} //------------------------------------------------------------------------
void MemPro::Disconnect()
{
if(gp_MemPro)
{
gp_MemPro->Lock();
gp_MemPro->Disconnect(true);
gp_MemPro->Release();
}
} //------------------------------------------------------------------------
void MemPro::Shutdown()
{
if(!g_ShuttingDown)
{
g_ShuttingDown = true;
if(gp_MemPro)
{
gp_MemPro->Lock();
gp_MemPro->Shutdown();
gp_MemPro->Release();
gp_MemPro->~CMemPro();
memset(gp_MemPro, , sizeof(CMemPro));
gp_MemPro = NULL;
}
}
} //------------------------------------------------------------------------
void MemPro::TrackAlloc(void* p, size_t size, bool wait_for_connect)
{
CMemPro* p_mempro = GetMemPro();
if(p_mempro)
p_mempro->TrackAlloc(p, size, wait_for_connect);
} //------------------------------------------------------------------------
void MemPro::TrackFree(void* p, bool wait_for_connect)
{
CMemPro* p_mempro = GetMemPro();
if(p_mempro)
p_mempro->TrackFree(p, wait_for_connect);
} //------------------------------------------------------------------------
void MemPro::SetPaused(bool paused)
{
CMemPro* p_mempro = GetMemPro();
if(p_mempro)
p_mempro->SetPaused(paused);
} //------------------------------------------------------------------------
bool MemPro::IsPaused()
{
CMemPro* p_mempro = GetMemPro();
return p_mempro ? p_mempro->IsPaused() : false;
} //------------------------------------------------------------------------
void MemPro::SendPageState(void* p, size_t size, PageState page_state, PageType page_type, unsigned int page_protection, bool send_memory)
{
CMemPro* p_mempro = GetMemPro();
if(p_mempro)
p_mempro->SendPageState(p, size, page_state, page_type, page_protection, send_memory);
} //------------------------------------------------------------------------
void MemPro::TakeSnapshot()
{
if(gp_MemPro) gp_MemPro->TakeSnapshot();
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO
//------------------------------------------------------------------------
// MemProLib.cpp //------------------------------------------------------------------------
namespace MemPro
{
int mempro_total_alloc = ; //------------------------------------------------------------------------
#if defined(MEMPRO_PLATFORM_XBOXONE)
#error Please contact slynch@puredevsoftware.com for this platform
#endif
}
//------------------------------------------------------------------------
// Socket.cpp #include <stdlib.h>
#include <new> #ifdef MEMPRO_WIN_BASED_PLATFORM
#include <tchar.h>
#endif //------------------------------------------------------------------------
#if defined(ENABLE_MEMPRO) && !defined(WRITE_DUMP) //------------------------------------------------------------------------
namespace MemPro
{
volatile int g_InitialiseCount = ;
} //------------------------------------------------------------------------
bool MemPro::Socket::InitialiseWSA()
{
if(g_InitialiseCount == )
{
#ifdef MEMPRO_PLATFORM_XBOX360
#error Please contact slynch@puredevsoftware.com for this platform
#endif #ifdef MEMPRO_WIN_BASED_PLATFORM
// Initialize Winsock
WSADATA wsaData;
if(WSAStartup(MAKEWORD(,), &wsaData) != )
{
HandleError();
return false;
}
#endif
} ++g_InitialiseCount; return true;
} //------------------------------------------------------------------------
void MemPro::Socket::CleanupWSA()
{
--g_InitialiseCount; if(g_InitialiseCount == )
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
if(WSACleanup() == SOCKET_ERROR)
HandleError();
#endif #ifdef MEMPRO_PLATFORM_XBOX360
#error Please contact slynch@puredevsoftware.com for this platform
#endif
}
} //------------------------------------------------------------------------
void MemPro::Socket::Disconnect()
{
if(m_Socket != INVALID_SOCKET)
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
if(shutdown(m_Socket, SD_BOTH) == SOCKET_ERROR)
HandleError();
#else
if(shutdown(m_Socket, SHUT_RDWR) == SOCKET_ERROR)
HandleError();
#endif // loop until the socket is closed to ensure all data is sent
unsigned int buffer = ;
size_t ret = ;
do { ret = recv(m_Socket, (char*)&buffer, sizeof(buffer), ); } while(ret != && ret != (size_t)SOCKET_ERROR); #ifdef MEMPRO_WIN_BASED_PLATFORM
if(closesocket(m_Socket) == SOCKET_ERROR)
HandleError();
#else
close(m_Socket);
#endif
m_Socket = INVALID_SOCKET;
}
} //------------------------------------------------------------------------
bool MemPro::Socket::StartListening()
{
MEMPRO_ASSERT(m_Socket != INVALID_SOCKET); if (listen(m_Socket, SOMAXCONN) == SOCKET_ERROR)
{
HandleError();
return false;
}
return true;
} //------------------------------------------------------------------------
bool MemPro::Socket::Bind(const char* p_port)
{
MEMPRO_ASSERT(m_Socket == INVALID_SOCKET); if(!InitialiseWSA())
return false; #ifdef MEMPRO_PLATFORM_WIN
// setup the addrinfo struct
addrinfo info;
ZeroMemory(&info, sizeof(info));
info.ai_family = AF_INET;
info.ai_socktype = SOCK_STREAM;
info.ai_protocol = IPPROTO_TCP;
info.ai_flags = AI_PASSIVE; // Resolve the server address and port
addrinfo* p_result_info;
HRESULT result = getaddrinfo(NULL, p_port, &info, &p_result_info);
if (result != )
{
HandleError();
return false;
} m_Socket = socket(
p_result_info->ai_family,
p_result_info->ai_socktype,
p_result_info->ai_protocol);
#else
m_Socket = socket(
AF_INET,
SOCK_STREAM,
IPPROTO_TCP);
#endif if (m_Socket == INVALID_SOCKET)
{
#ifdef MEMPRO_PLATFORM_WIN
freeaddrinfo(p_result_info);
#endif
HandleError();
return false;
} // Setup the TCP listening socket
#ifdef MEMPRO_PLATFORM_WIN
result = ::bind(m_Socket, p_result_info->ai_addr, (int)p_result_info->ai_addrlen);
freeaddrinfo(p_result_info);
#else
// Bind to INADDR_ANY
SOCKADDR_IN sa;
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = INADDR_ANY;
int iport = atoi(p_port);
sa.sin_port = htons(iport);
int result = ::bind(m_Socket, (const sockaddr*)(&sa), sizeof(SOCKADDR_IN));
#endif if (result == SOCKET_ERROR)
{
HandleError();
Disconnect();
return false;
} return true;
} //------------------------------------------------------------------------
bool MemPro::Socket::Accept(Socket& client_socket)
{
MEMPRO_ASSERT(client_socket.m_Socket == INVALID_SOCKET);
client_socket.m_Socket = accept(m_Socket, NULL, NULL);
return client_socket.m_Socket != INVALID_SOCKET;
} //------------------------------------------------------------------------
bool MemPro::Socket::Send(void* p_buffer, int size)
{
int bytes_to_send = size;
while(bytes_to_send != )
{
int bytes_sent = (int)send(m_Socket, (char*)p_buffer, bytes_to_send, );
if(bytes_sent == SOCKET_ERROR)
{
HandleError();
Disconnect();
return false;
}
p_buffer = (char*)p_buffer + bytes_sent;
bytes_to_send -= bytes_sent;
} return true;
} //------------------------------------------------------------------------
int MemPro::Socket::Receive(void* p_buffer, int size)
{
int total_bytes_received = ;
while(size)
{
int bytes_received = (int)recv(m_Socket, (char*)p_buffer, size, ); total_bytes_received += bytes_received; if(bytes_received == )
{
Disconnect();
return bytes_received;
}
else if(bytes_received == SOCKET_ERROR)
{
HandleError();
Disconnect();
return bytes_received;
} size -= bytes_received;
p_buffer = (char*)p_buffer + bytes_received;
} return total_bytes_received;
} //------------------------------------------------------------------------
void MemPro::Socket::HandleError()
{
#ifdef MEMPRO_PLATFORM_WIN
if(WSAGetLastError() == WSAEADDRINUSE)
{
OutputDebugString(_T("MemPro: Network connection conflict. Please make sure that other MemPro enabled applications are shut down, or change the port in the the MemPro lib and MemPro settings.\n"));
return;
} TCHAR* p_buffer = NULL;
va_list args;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
WSAGetLastError(),
,
(TCHAR*)&p_buffer,
* ,
&args); OutputDebugString(p_buffer); LocalFree(p_buffer);
#endif
} //------------------------------------------------------------------------
#endif // #if defined(ENABLE_MEMPRO) && !defined(WRITE_DUMP)
//------------------------------------------------------------------------
// Thread.cpp //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO #ifdef MEMPRO_UNIX_BASED_PLATFORM
#include <pthread.h>
#endif //------------------------------------------------------------------------
MemPro::Thread::Thread()
#ifdef MEMPRO_WIN_BASED_PLATFORM
: m_Handle(),
m_Alive(false)
#else
: m_Alive(false)
#endif
{
} //------------------------------------------------------------------------
void MemPro::Thread::CreateThread(ThreadMain p_thread_main, void* p_param)
{
mp_ThreadMain = p_thread_main;
mp_Param = p_param; #ifdef MEMPRO_WIN_BASED_PLATFORM
m_Handle = ::CreateThread(NULL, , PlatformThreadMain, this, , NULL);
#else
pthread_create(&m_Thread, NULL, PlatformThreadMain, this);
#endif
} //------------------------------------------------------------------------
#ifdef MEMPRO_WIN_BASED_PLATFORM
unsigned long WINAPI MemPro::Thread::PlatformThreadMain(void* p_param)
{
Thread* p_thread = (Thread*)p_param;
p_thread->m_Alive = true;
unsigned long ret = (unsigned long)p_thread->mp_ThreadMain(p_thread->mp_Param);
p_thread->m_Alive = false;
return ret;
}
#else
void* MemPro::Thread::PlatformThreadMain(void* p_param)
{
Thread* p_thread = (Thread*)p_param;
p_thread->m_Alive = true;
p_thread->mp_ThreadMain(p_thread->mp_Param);
p_thread->m_Alive = false;
return NULL;
}
#endif //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPROMemPro.cpp
这两个是官方原始的1.3.7.0的代码,关于检测内存泄露比较具有研究价值;
- 整合并入UE4的方法, 这里先只有windows和XBoxOne平台,修改后的MemPro.hpp和MemPro.cpp:
-
/*
This software is provided 'as-is', without any express or implied warranty.
In no event will the author(s) be held liable for any damages arising from
the use of this software. Permission is granted to anyone to use this software for any purpose, including
commercial applications, and to alter it and redistribute it freely, subject to
the following restrictions: 1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution. Author: Stewart Lynch
www.puredevsoftware.com
slynch@puredevsoftware.com This code is released to the public domain, as explained at
http://creativecommons.org/publicdomain/zero/1.0/ MemProLib is the library that allows the MemPro application to communicate
with your application. ===========================================================
SETUP
=========================================================== * include MemPro.cpp and MemPro.hpp into your project. * Link with Dbghelp.lib and Ws2_32.lib - these are needed for the callstack trace and the network connection * Connect to your app with the MemPro
*/ //------------------------------------------------------------------------
// MemPro.hpp
//------------------------------------------------------------------------
/*
MemPro
Version: 1.3.7.0
*/
//------------------------------------------------------------------------
#ifndef MEMPRO_MEMPRO_H_INCLUDED
#define MEMPRO_MEMPRO_H_INCLUDED //------------------------------------------------------------------------
//#define ENABLE_MEMPRO // **** enable/disable MemPro here! **** //@Virtuos[wangsongwei] disable it here
//@Virtuos[wangsongwei] begin: add new macro ENABLE_VIRTUOS_MEMPRO_UE4 to support integrate MemPro into UE4, open it in file Build.h
#ifdef ENABLE_VIRTUOS_MEMPRO_UE4
#define ENABLE_MEMPRO //comment this line to disable // #if PLATFORM_XBOXONE
// #define WAIT_FOR_CONNECT
// #define WAIT_FOR_CONNECT true
// #endif #endif
//@Virtuos[wangsongwei]end //------------------------------------------------------------------------
// macros for tracking allocs that define to nothing if disabled
#ifdef ENABLE_MEMPRO
#ifndef WAIT_FOR_CONNECT
#define WAIT_FOR_CONNECT false
#endif
#define MEMPRO_TRACK_ALLOC(p, size) MemPro::TrackAlloc(p, size, WAIT_FOR_CONNECT)
#define MEMPRO_TRACK_FREE(p) MemPro::TrackFree(p, WAIT_FOR_CONNECT)
#else
#define MEMPRO_TRACK_ALLOC(p, size) ((void)0)
#define MEMPRO_TRACK_FREE(p) ((void)0)
#endif //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
// Some platforms have problems initialising winsock from global constructors,
// to help get around this problem MemPro waits this amount of time before
// initialising. Allocs and freed that happen during this time are stored in
// a temporary buffer.
#define MEMPRO_INIT_DELAY 100 //------------------------------------------------------------------------
// MemPro waits this long before giving up on a connection after initialisation
#define MEMPRO_CONNECT_TIMEOUT 500
//#define MEMPRO_CONNECT_TIMEOUT 1000 //@Virtuos[wangsongwei] define wait time out is one second, if this time is too long, it will cause x1 crash with timeout loading //------------------------------------------------------------------------
#include <stdlib.h> //------------------------------------------------------------------------
//#define WRITE_DUMP _T("allocs.mempro_dump") //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
enum PageState
{
Invalid = -,
Free,
Reserved,
Committed
}; //------------------------------------------------------------------------
enum PageType
{
page_Unknown = -,
page_Image,
page_Mapped,
page_Private
}; //------------------------------------------------------------------------ // You don't need to call this directly, it is automatically called on the first allocation.
// Only call this function if you want to be able to connect to your app before it has allocated any memory.
// If wait_for_connect is true this function will block until the external MemPro app has connected,
// this is useful to make sure that every single allocation is being tracked.
void Initialise(bool wait_for_connect=false); void Disconnect(); // kick all current connections, but can accept more void Shutdown(); // free all resources, no more connections allowed void TrackAlloc(void* p, size_t size, bool wait_for_connect=false); void TrackFree(void* p, bool wait_for_connect=false); bool IsPaused(); void SetPaused(bool paused); // this is used for the realtime memory graph.
void SendPageState(void* p, size_t size, PageState page_state, PageType page_type, unsigned int page_protection, bool send_memory); void TakeSnapshot(); // ignore these, for internal use only
void IncRef();
void DecRef();
} //------------------------------------------------------------------------
#ifndef WRITE_DUMP
namespace
{
// if we are using sockets we need to flush the sockets on global teardown
// This class is a trick to attempt to get mempro to shutdown after all other
// global objects.
class MemProGLobalScope
{
public:
MemProGLobalScope() { MemPro::IncRef(); }
~MemProGLobalScope() { MemPro::DecRef(); }
};
static MemProGLobalScope g_MemProGLobalScope;
}
#endif //------------------------------------------------------------------------
#ifndef ENABLE_VIRTUOS_MEMPRO_UE4//@Virtuos[wangsongwei] UE4 don't need override these new/delete/malloc/free operator here
#ifdef OVERRIDE_NEW_DELETE #if defined(__APPLE__)
// if you get linker errors about duplicatly defined symbols please add a unexport.txt
// file to your build settings
// see here: https://developer.apple.com/library/mac/technotes/tn2185/_index.html
void* operator new(std::size_t size) throw(std::bad_alloc)
{
void* p = malloc(size);
MEMPRO_TRACK_ALLOC(p, size);
return p;
} void* operator new(std::size_t size, const std::nothrow_t&) throw()
{
void* p = malloc(size);
MEMPRO_TRACK_ALLOC(p, size);
return p;
} void operator delete(void* p) throw()
{
MEMPRO_TRACK_FREE(p);
free(p);
} void operator delete(void* p, const std::nothrow_t&) throw()
{
MEMPRO_TRACK_FREE(p);
free(p);
} void* operator new[](std::size_t size) throw(std::bad_alloc)
{
void* p = malloc(size);
MEMPRO_TRACK_ALLOC(p, size);
return p;
} void* operator new[](std::size_t size, const std::nothrow_t&) throw()
{
void* p = malloc(size);
MEMPRO_TRACK_ALLOC(p, size);
return p;
} void operator delete[](void* p) throw()
{
MEMPRO_TRACK_FREE(p);
free(p);
} void operator delete[](void* p, const std::nothrow_t&) throw()
{
MEMPRO_TRACK_FREE(p);
free(p);
}
#else
#include <malloc.h> void* operator new(size_t size)
{
void* p = malloc(size);
MEMPRO_TRACK_ALLOC(p, size);
return p;
} void operator delete(void* p)
{
MEMPRO_TRACK_FREE(p);
free(p);
} void* operator new[](size_t size)
{
void* p = malloc(size);
MEMPRO_TRACK_ALLOC(p, size);
return p;
} void operator delete[](void* p)
{
MEMPRO_TRACK_FREE(p);
free(p);
}
#endif #endif //------------------------------------------------------------------------
#ifdef OVERRIDE_MALLOC_FREE #if defined(_WIN32) || defined(_WIN64) || defined(WIN32) || defined(WIN64) || defined(__WIN32__) || defined(__WINDOWS__) // NOTE: for this to work, you will need to make sure you are linking STATICALLY to the crt. eg: /MTd __declspec(restrict) __declspec(noalias) void* malloc(size_t size)
{
void* p = HeapAlloc(GetProcessHeap(), , size);
MEMPRO_TRACK_ALLOC(p, size);
return p;
} __declspec(restrict) __declspec(noalias) void* realloc(void *p, size_t new_size)
{
MEMPRO_TRACK_FREE(p);
void* p_new = HeapReAlloc(GetProcessHeap(), , p, new_size);
MEMPRO_TRACK_ALLOC(p_new, new_size);
return p_new;
} __declspec(noalias) void free(void *p)
{
HeapFree(GetProcessHeap(), , p);
MEMPRO_TRACK_FREE(p);
}
#else
void *malloc(int size)
{
void* (*ptr)(int);
void* handle = (void*)-;
ptr = (void*)dlsym(handle, "malloc");
if(!ptr) abort();
void *p = (*ptr)(size);
MEMPRO_TRACK_ALLOC(p, size);
return p;
} void *realloc(void *p, int size)
{
MEMPRO_TRACK_FREE(p);
void * (*ptr)(void *, int);
void * handle = (void*) -;
ptr = (void*)dlsym(handle, "realloc");
if (!ptr) abort();
void* p_new = (*ptr)(p, size);
MEMPRO_TRACK_ALLOC(p_new, size);
return p_new;
} void free(void *p)
{
MEMPRO_TRACK_FREE(p);
void* (*ptr)(void*);
void* handle = (void*)-;
ptr = (void*)dlsym(handle, "free");
if (!ptr == NULL) abort();
(*ptr)(alloc);
}
#endif
#endif
#endif//@Virtuos[wangsongwei] #ifndef ENABLE_VIRTUOS_MEMPRO_UE4
//------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_MEMPRO_H_INCLUDEDMemPro.hpp
/*
This software is provided 'as-is', without any express or implied warranty.
In no event will the author(s) be held liable for any damages arising from
the use of this software. Permission is granted to anyone to use this software for any purpose, including
commercial applications, and to alter it and redistribute it freely, subject to
the following restrictions: 1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution. Author: Stewart Lynch
www.puredevsoftware.com
slynch@puredevsoftware.com This code is released to the public domain, as explained at
http://creativecommons.org/publicdomain/zero/1.0/ MemProLib is the library that allows the MemPro application to communicate
with your application.
*/ //@Virtuos[wangsongwei]begin: enable MemPro's original UE4 macro
#ifdef ENABLE_VIRTUOS_MEMPRO_UE4
#define FRAMEPRO_TOOLSET_UE4 1 //comment this line to disable UE4
#endif
//@Virtuos[wangsongwei]end #if FRAMEPRO_TOOLSET_UE4
#include "CorePrivatePCH.h"
#endif #include "MemPro.hpp" //------------------------------------------------------------------------
// CallstackSet.cpp //------------------------------------------------------------------------
// MemProLib.hpp
#ifndef MEMPRO_MEMPROLIB_H_INCLUDED
#define MEMPRO_MEMPROLIB_H_INCLUDED //------------------------------------------------------------------------ //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
// **** The Target Platform **** // define ONE of these #if defined(_WIN32) || defined(_WIN64) || defined(WIN32) || defined(WIN64) || defined(__WIN32__) || defined(__WINDOWS__)
#if PLATFORM_XBOXONE || defined(_XBOX_ONE) //@Virtuos[wangsongwei] also add UE4's xboxone macro
#define MEMPRO_PLATFORM_XBOXONE
//#elif defined(_XBOX)
// #define MEMPRO_PLATFORM_XBOX360 //@Virtuos[wangsongwei] not Xbox360 any more
#elif PLATFORM_WINDOWS //@Virtuos[wangsongwei]add UE4's windows macro
#define MEMPRO_PLATFORM_WIN
#endif
#elif defined(__APPLE__)
#define MEMPRO_PLATFORM_APPLE
#else
#define MEMPRO_PLATFORM_UNIX //@Virtuos[wangsongwei] todo, PS4 should be here
#endif //------------------------------------------------------------------------
#if defined(MEMPRO_PLATFORM_WIN) || defined(MEMPRO_PLATFORM_XBOX360) || defined(MEMPRO_PLATFORM_XBOXONE)
#define MEMPRO_WIN_BASED_PLATFORM
#endif //------------------------------------------------------------------------
#if defined(MEMPRO_PLATFORM_UNIX) || defined(MEMPRO_PLATFORM_APPLE)
#define MEMPRO_UNIX_BASED_PLATFORM
#endif //------------------------------------------------------------------------
#if defined(MEMPRO_PLATFORM_WIN)
#if (defined(FRAMEPRO_TOOLSET_UE4) && FRAMEPRO_TOOLSET_UE4)
#include "AllowWindowsPlatformTypes.h"
#endif
#ifndef WRITE_DUMP
#if defined(UNICODE) && !defined(_UNICODE)
#error for unicode builds please define both UNICODE and _UNICODE. See the FAQ for more details.
#endif
#if defined(AF_IPX) && !defined(_WINSOCK2API_)
#error winsock already defined. Please include winsock2.h before including windows.h or use WIN32_LEAN_AND_MEAN. See the FAQ for more info.
#endif
#define WIN32_LEAN_AND_MEAN #pragma warning(push)
#pragma warning(disable : 4668)
#include <winsock2.h>
#pragma warning(pop) #include <ws2tcpip.h>
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
#include <intrin.h>
#endif
#if (defined(FRAMEPRO_TOOLSET_UE4) && FRAMEPRO_TOOLSET_UE4)
#include "HideWindowsPlatformTypes.h"
#endif
#elif defined(MEMPRO_PLATFORM_XBOX360)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_PLATFORM_XBOXONE)
//#error Please contact slynch@puredevsoftware.com for this platform //@Virtuos[wangsongwei] I don't know what I need to do here, just annotate this line avoid compile error, maybe need include some necessary header files
#if (defined(FRAMEPRO_TOOLSET_UE4) && FRAMEPRO_TOOLSET_UE4)
#include "AllowXboxOnePlatformTypes.h"//@Virtuos[wangsongwei] add code support X1 version
#endif
#ifndef WRITE_DUMP
//@Virtuos[wangsongwei] XboxOne should use same header file as Windows's
#if defined(UNICODE) && !defined(_UNICODE)
#error for unicode builds please define both UNICODE and _UNICODE. See the FAQ for more details.
#endif
#if defined(AF_IPX) && !defined(_WINSOCK2API_)
#error winsock already defined. Please include winsock2.h before including windows.h or use WIN32_LEAN_AND_MEAN. See the FAQ for more info.
#endif
#define WIN32_LEAN_AND_MEAN #pragma warning(push)
#pragma warning(disable : 4668)
#include <winsock2.h>
#pragma warning(pop) #include <ws2tcpip.h>
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
#include <intrin.h>
#endif
#if (defined(FRAMEPRO_TOOLSET_UE4) && FRAMEPRO_TOOLSET_UE4)
#include "HideXboxOnePlatformTypes.h"//@Virtuos[wangsongwei] add code support X1 version
#endif
#elif defined(MEMPRO_UNIX_BASED_PLATFORM)//@Virtuos[wangsongwei] PS4 should be here
#include <execinfo.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#else
#error platform not defined
#endif //------------------------------------------------------------------------
#if defined(_WIN64) || defined(__LP64__) || defined(__x86_64__) || defined(__ppc64__)
#define MEMPRO64
#endif //------------------------------------------------------------------------
#if defined(MEMPRO_PLATFORM_XBOXONE) && !defined(MEMPRO64)
#error Please contact slynch@puredevsoftware.com for this platform
//@Virtuos[wangsongwei] todo
#endif //------------------------------------------------------------------------
#if defined(MEMPRO_WIN_BASED_PLATFORM)
#define MEMPRO_ASSERT(b) if(!(b)) DebugBreak()
#elif defined(MEMPRO_UNIX_BASED_PLATFORM)
#define MEMPRO_ASSERT(b) if(!(b)) __builtin_trap()
#else
#error // platform not defined
#endif //------------------------------------------------------------------------
#define MEMPRO_STATIC_ASSERT(expr) typedef char STATIC_ASSERT_TEST[ (expr) ] //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
class Allocator
{
public:
#if defined(MEMPRO_PLATFORM_XBOXONE)
//#error Please contact slynch@puredevsoftware.com for this platform
//@Virtuos[wangsongwei]begin: XboxOne also has these function
static void* Alloc(int size)
{
return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
}
static void Free(void* p, int size)
{
VirtualFree(p, size, MEM_RELEASE);
}
//@Virtuos[wangsongwei]end
#elif defined(MEMPRO_WIN_BASED_PLATFORM)//@Virtuos[wangsongwei] in fact xboxone also enable this macro
static void* Alloc(int size)
{
return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
}
static void Free(void* p, int size)
{
VirtualFree(p, size, MEM_RELEASE);
}
#else
static void* Alloc(int size) { return malloc(size); }
static void Free(void* p, int size) { free(p); }
#endif
}; //------------------------------------------------------------------------
#if defined(MEMPRO_WIN_BASED_PLATFORM)
typedef __int64 int64;
typedef unsigned __int64 uint64;
#elif defined(MEMPRO_UNIX_BASED_PLATFORM)
typedef long long int64;
typedef unsigned long long uint64;
#else
#error
#endif //------------------------------------------------------------------------
// platform specific stuff
#if defined(MEMPRO_WIN_BASED_PLATFORM)
#define MEMPRO_FORCEINLINE FORCEINLINE
#else
#define MEMPRO_FORCEINLINE inline
void memcpy_s(void* p_dst, int dst_len, void* p_src, int copy_len) { memcpy(p_dst, p_src, copy_len); }
void Sleep(int ms) { usleep( * ms); }
typedef int SOCKET;
typedef int DWORD;
enum SocketValues { INVALID_SOCKET = - };
#ifndef UINT_MAX
enum MaxValues { UINT_MAX = 0xffffffff };
#endif
void OutputDebugString(const char*) {}
#define _T(s) s
enum SocketErrorCodes { SOCKET_ERROR = - };
typedef sockaddr_in SOCKADDR_IN;
enum SystemDefines { MAX_PATH = };
#endif
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_MEMPROLIB_H_INCLUDED //------------------------------------------------------------------------
// CallstackSet.hpp
#ifndef MEMPRO_CALLSTACKSET_H_INCLUDED
#define MEMPRO_CALLSTACKSET_H_INCLUDED //------------------------------------------------------------------------ //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
struct Callstack
{
uint64* mp_Stack;
int m_ID;
int m_Size;
unsigned int m_Hash;
}; //------------------------------------------------------------------------
// A hash set collection for Callstack structures. Callstacks are added and
// retreived using the stack address array as the key.
// This class only allocates memory using virtual alloc/free to avoid going
// back into the mian allocator.
class CallstackSet
{
public:
CallstackSet(); ~CallstackSet(); Callstack* Get(uint64* p_stack, int stack_size, unsigned int hash); Callstack* Add(uint64* p_stack, int stack_size, unsigned int hash); void Clear(); private:
void Grow(); void Add(Callstack* p_callstack); //------------------------------------------------------------------------
// data
private:
Callstack** mp_Data;
unsigned int m_CapacityMask;
int m_Count;
int m_Capacity;
};
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_CALLSTACKSET_H_INCLUDED //------------------------------------------------------------------------
// BlockAllocator.hpp
#ifndef MEMPRO_MEMPRO_SPINLOCK_H_INCLUDED
#define MEMPRO_MEMPRO_SPINLOCK_H_INCLUDED //------------------------------------------------------------------------ //------------------------------------------------------------------------
// disable some warnings we are not interested in so that we can compile at warning level4
#ifdef MEMPRO_WIN_BASED_PLATFORM
#pragma warning(disable : 4100)
#endif //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
// a very simple allocator tat allocated blocks of 64k of memory using the
// templatized allocator.
template<class TAllocator>
class BlockAllocator
{
public:
inline BlockAllocator(); inline void* Alloc(int size); inline void Free(void* p); //------------------------------------------------------------------------
// data
private:
static const int m_BlockSize = *;
void* mp_CurBlock;
int m_CurBlockUsage;
}; //------------------------------------------------------------------------
template<class TAllocator>
BlockAllocator<TAllocator>::BlockAllocator()
: mp_CurBlock(NULL),
m_CurBlockUsage()
{
} //------------------------------------------------------------------------
template<class TAllocator>
void* BlockAllocator<TAllocator>::Alloc(int size)
{
MEMPRO_ASSERT(size < m_BlockSize); if(!mp_CurBlock || size > m_BlockSize - m_CurBlockUsage)
{
mp_CurBlock = TAllocator::Alloc(m_BlockSize);
MEMPRO_ASSERT(mp_CurBlock);
m_CurBlockUsage = ;
} void* p = (char*)mp_CurBlock + m_CurBlockUsage;
m_CurBlockUsage += size; return p;
} //------------------------------------------------------------------------
template<class TAllocator>
void BlockAllocator<TAllocator>::Free(void* p)
{
// do nothing
}
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_MEMPRO_SPINLOCK_H_INCLUDED //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
const int g_InitialCapacity = ; // must be a power of 2 MemPro::BlockAllocator<Allocator> g_BlockAllocator; //------------------------------------------------------------------------
inline bool StacksMatch(MemPro::Callstack* p_callstack, uint64* p_stack, int stack_size, unsigned int hash)
{
if(p_callstack->m_Size != stack_size)
return false; if(p_callstack->m_Hash != hash)
return false; for(int i=; i<stack_size; ++i)
if(p_callstack->mp_Stack[i] != p_stack[i])
return false; return true;
}
} //------------------------------------------------------------------------
MemPro::CallstackSet::CallstackSet()
: mp_Data((Callstack**)Allocator::Alloc(g_InitialCapacity*sizeof(Callstack*))),
m_CapacityMask(g_InitialCapacity-),
m_Count(),
m_Capacity(g_InitialCapacity)
{
memset(mp_Data, , g_InitialCapacity*sizeof(Callstack*));
} //------------------------------------------------------------------------
MemPro::CallstackSet::~CallstackSet()
{
Clear();
} //------------------------------------------------------------------------
void MemPro::CallstackSet::Grow()
{
int old_capacity = m_Capacity;
Callstack** p_old_data = mp_Data; // allocate a new set
m_Capacity *= ;
m_CapacityMask = m_Capacity - ;
int size = m_Capacity * sizeof(Callstack*);
mp_Data = (Callstack**)Allocator::Alloc(size);
memset(mp_Data, , size); // transfer callstacks from old set
m_Count = ;
for(int i=; i<old_capacity; ++i)
{
Callstack* p_callstack = p_old_data[i];
if(p_callstack)
Add(p_callstack);
} // release old buffer
Allocator::Free(p_old_data, old_capacity*sizeof(Callstack*));
} //------------------------------------------------------------------------
MemPro::Callstack* MemPro::CallstackSet::Get(uint64* p_stack, int stack_size, unsigned int hash)
{
int index = hash & m_CapacityMask; while(mp_Data[index] && !StacksMatch(mp_Data[index], p_stack, stack_size, hash))
index = (index + ) & m_CapacityMask; return mp_Data[index];
} //------------------------------------------------------------------------
MemPro::Callstack* MemPro::CallstackSet::Add(uint64* p_stack, int stack_size, unsigned int hash)
{
// grow the set if necessary
if(m_Count > m_Capacity/)
Grow(); // create a new callstack
Callstack* p_callstack = (Callstack*)g_BlockAllocator.Alloc(sizeof(Callstack));
p_callstack->m_ID = m_Count;
p_callstack->m_Size = stack_size;
p_callstack->mp_Stack = (uint64*)g_BlockAllocator.Alloc(stack_size*sizeof(uint64));
p_callstack->m_Hash = hash;
memcpy_s(p_callstack->mp_Stack, stack_size*sizeof(uint64), p_stack, stack_size*sizeof(uint64)); Add(p_callstack); return p_callstack;
} //------------------------------------------------------------------------
void MemPro::CallstackSet::Add(Callstack* p_callstack)
{
// find a clear index
int index = p_callstack->m_Hash & m_CapacityMask;
while(mp_Data[index])
index = (index + ) & m_CapacityMask; mp_Data[index] = p_callstack; ++m_Count;
} //------------------------------------------------------------------------
void MemPro::CallstackSet::Clear()
{
for(int i=; i<m_Capacity; ++i)
{
if(mp_Data[i])
g_BlockAllocator.Free(mp_Data[i]);
} Allocator::Free(mp_Data, m_Capacity*sizeof(Callstack*)); size_t size = g_InitialCapacity*sizeof(Callstack*);
mp_Data = (Callstack**)Allocator::Alloc((int)size);
memset(mp_Data, , size);
m_CapacityMask = g_InitialCapacity-;
m_Count = ;
m_Capacity = g_InitialCapacity;
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO
//------------------------------------------------------------------------
// MemPro.cpp //------------------------------------------------------------------------
// RingBuffer.hpp
#ifndef MEMPRO_RINGBUFFER_H_INCLUDED
#define MEMPRO_RINGBUFFER_H_INCLUDED //------------------------------------------------------------------------ //------------------------------------------------------------------------
// CriticalSection.hpp
#ifndef MEMPRO_CRITICALSECTION_H_INCLUDED
#define MEMPRO_CRITICALSECTION_H_INCLUDED //------------------------------------------------------------------------ //------------------------------------------------------------------------
#ifdef MEMPRO_UNIX_BASED_PLATFORM
#include <pthread.h>
#endif //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
class CriticalSection
{
public:
CriticalSection()
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
InitializeCriticalSection(&cs);
#else
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&cs, &attr);
#endif
} ~CriticalSection()
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
DeleteCriticalSection(&cs);
#else
pthread_mutex_destroy(&cs);
#endif
} void Enter()
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
EnterCriticalSection(&cs);
#else
pthread_mutex_lock(&cs);
#endif
} void Leave()
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
LeaveCriticalSection(&cs);
#else
pthread_mutex_unlock(&cs);
#endif
}
private: //------------------------------------------------------------------------
// data
private:
#ifdef MEMPRO_WIN_BASED_PLATFORM
CRITICAL_SECTION cs;
#else
pthread_mutex_t cs;
#endif
}; //------------------------------------------------------------------------
class CriticalSectionScope
{
public:
CriticalSectionScope(CriticalSection& in_cs) : cs(in_cs) { cs.Enter(); }
~CriticalSectionScope() { cs.Leave(); }
private:
CriticalSectionScope(const CriticalSectionScope&);
CriticalSectionScope& operator=(const CriticalSectionScope&);
CriticalSection& cs;
};
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_CRITICALSECTION_H_INCLUDED //------------------------------------------------------------------------
#ifdef MEMPRO_WIN_BASED_PLATFORM
// USE_INTRINSIC can also be enabled on 32bit platform, but I left it disabled because it doesn't work on XP
#ifdef MEMPRO64
#define USE_INTRINSIC
#endif
#endif #ifdef USE_INTRINSIC
#include <intrin.h>
#pragma intrinsic(_InterlockedCompareExchange64)
#pragma intrinsic(_InterlockedExchangeAdd64)
#endif //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------ //------------------------------------------------------------------------
// Event.hpp
#ifndef MEMPRO_EVENT_H_INCLUDED
#define MEMPRO_EVENT_H_INCLUDED //------------------------------------------------------------------------ //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
namespace MemPro
{
//--------------------------------------------------------------------
class Event
{
public:
//--------------------------------------------------------------------
Event(bool initial_state, bool auto_reset)
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
m_Handle = CreateEvent(NULL, !auto_reset, initial_state, NULL);
#else
pthread_cond_init(&m_Cond, NULL);
pthread_mutex_init(&m_Mutex, NULL);
m_Signalled = false;
m_AutoReset = auto_reset; if(initial_state)
Set();
#endif
} //--------------------------------------------------------------------
~Event()
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
CloseHandle(m_Handle);
#else
pthread_mutex_destroy(&m_Mutex);
pthread_cond_destroy(&m_Cond);
#endif
} //--------------------------------------------------------------------
void Set() const
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
SetEvent(m_Handle);
#else
pthread_mutex_lock(&m_Mutex);
m_Signalled = true;
pthread_mutex_unlock(&m_Mutex);
pthread_cond_signal(&m_Cond);
#endif
} //--------------------------------------------------------------------
void Reset()
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
ResetEvent(m_Handle);
#else
pthread_mutex_lock(&m_Mutex);
m_Signalled = false;
pthread_mutex_unlock(&m_Mutex);
#endif
} //--------------------------------------------------------------------
int Wait(int timeout=-) const
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
MEMPRO_STATIC_ASSERT(INFINITE == -);
return WaitForSingleObject(m_Handle, timeout) == /*WAIT_OBJECT_0*/;
#else
pthread_mutex_lock(&m_Mutex); if(m_Signalled)
{
m_Signalled = false;
pthread_mutex_unlock(&m_Mutex);
return true;
} if(timeout == -)
{
while(!m_Signalled)
pthread_cond_wait(&m_Cond, &m_Mutex); if(!m_AutoReset)
m_Signalled = false; pthread_mutex_unlock(&m_Mutex); return true;
}
else
{
timeval curr;
gettimeofday(&curr, NULL); timespec time;
time.tv_sec = curr.tv_sec + timeout / ;
time.tv_nsec = (curr.tv_usec * ) + ((timeout % ) * ); pthread_cond_timedwait(&m_Cond, &m_Mutex, &time); if(m_Signalled)
{
if(!m_AutoReset)
m_Signalled = false; pthread_mutex_unlock(&m_Mutex);
return true;
} pthread_mutex_unlock(&m_Mutex);
return false;
}
#endif
} //------------------------------------------------------------------------
// data
private:
#ifdef MEMPRO_WIN_BASED_PLATFORM
HANDLE m_Handle;
#else
mutable pthread_cond_t m_Cond;
mutable pthread_mutex_t m_Mutex;
mutable volatile bool m_Signalled;
bool m_AutoReset;
#endif
};
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_EVENT_H_INCLUDED //------------------------------------------------------------------------
//#define USE_CRITICAL_SECTIONS //------------------------------------------------------------------------
namespace MemPro
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
#ifndef USE_INTRINSIC
//------------------------------------------------------------------------
MEMPRO_FORCEINLINE int64 ssInterlockedCompareExchange64(int64 volatile *dest, int64 exchange, int64 comperand)
{
__asm
{
lea esi,comperand;
lea edi,exchange;
mov eax,[esi];
mov edx,[esi];
mov ebx,[edi];
mov ecx,[edi];
mov esi,dest;
lock CMPXCHG8B [esi];
}
} //------------------------------------------------------------------------
MEMPRO_FORCEINLINE int64 ssInterlockedExchangeAdd64(__inout int64 volatile *Addend, __in int64 Value)
{
int64 Old;
do
{
Old = *Addend;
} while (ssInterlockedCompareExchange64(Addend, Old + Value, Old) != Old);
return Old;
} //------------------------------------------------------------------------
#define _InterlockedCompareExchange64 ssInterlockedCompareExchange64
#define _InterlockedExchangeAdd64 ssInterlockedExchangeAdd64
#endif
#else
// no interlocked functions, so just use a critical section
CriticalSection g_CASCritSec;
MEMPRO_FORCEINLINE int64 _InterlockedCompareExchange64(int64 volatile *dest, int64 exchange, int64 comperand)
{
g_CASCritSec.Enter();
int64 old_value = *dest;
if(*dest == comperand)
*dest = exchange;
g_CASCritSec.Leave();
return old_value;
} MEMPRO_FORCEINLINE int64 _InterlockedExchangeAdd64(int64 volatile *Addend, int64 Value)
{
g_CASCritSec.Enter();
int64 old_value = *Addend;
*Addend += Value;
g_CASCritSec.Leave();
return old_value;
}
#endif //------------------------------------------------------------------------
// This ring buffer is a lockless buffer, designed to be accessed by no more
// than two threads, one thread adding to the buffer and one removing. The
// threads first request data and then add or remove tat data. The threads
// will sleep if there is no space to add or no data to remove. Once the
// threads have space on the buffer the data can be added or removed.
class RingBuffer
{
public:
//------------------------------------------------------------------------
struct Range
{
Range() {}
Range(void* p, int s) : mp_Buffer(p), m_Size(s) {} void* mp_Buffer;
int m_Size;
}; //------------------------------------------------------------------------
RingBuffer(char* p_buffer, int size)
: m_Size(size),
mp_Buffer(p_buffer),
m_UsedRange(),
m_BytesRemovedEvent(false, true),
m_BytesAddedEvent(false, true)
{
MEMPRO_ASSERT(IsPow2(size)); #ifdef MEMPRO_WIN_BASED_PLATFORM
MEMPRO_ASSERT((((int64)&m_UsedRange) & ) == );
#endif #ifdef USE_CRITICAL_SECTIONS
InitializeCriticalSection(&m_CriticalSection);
#endif
} //------------------------------------------------------------------------
inline bool IsPow2(int value)
{
return (value & (value-)) == ;
} //------------------------------------------------------------------------
int GetSize() const
{
return m_Size;
} //------------------------------------------------------------------------
void Lock() const
{
#ifdef USE_CRITICAL_SECTIONS
EnterCriticalSection(&m_CriticalSection);
#endif
} //------------------------------------------------------------------------
void Release() const
{
#ifdef USE_CRITICAL_SECTIONS
LeaveCriticalSection(&m_CriticalSection);
#endif
} //------------------------------------------------------------------------
int64 GetRangeAtomic() const
{
#ifdef USE_CRITICAL_SECTIONS
Lock();
int64 range = m_UsedRange;
Release();
#else
// there must be a better way to atomically read a 64 bit value.
int64 range = _InterlockedExchangeAdd64(const_cast<int64*>(&m_UsedRange), );
#endif
return range;
} //------------------------------------------------------------------------
// return the largest free range possible
Range GetFreeRange(int timeout=-) const
{
int64 range = GetRangeAtomic();
int size = (int)(range & 0xffffffff); // wait until there is some space
while(size == m_Size)
{
if(!m_BytesRemovedEvent.Wait(timeout))
return Range(NULL, ); range = GetRangeAtomic();
size = (int)(range & 0xffffffff);
} int start = (int)((range >> ) & 0xffffffff); // calculate the size
int free_start = (start + size) & (m_Size-);
int free_size = free_start < start ? start - free_start : m_Size - free_start; return Range(mp_Buffer + free_start, free_size);
} //------------------------------------------------------------------------
// return the largest used range
Range GetAllocatedRange(int timeout=-) const
{
int64 range = GetRangeAtomic();
#ifdef _XBOX
__lwsync(); // ensure that the allocated data has finished writing
#endif
int size = (int)(range & 0xffffffff); // wait until there is some data
while(!size)
{
if(!m_BytesAddedEvent.Wait(timeout))
return Range(NULL, ); range = GetRangeAtomic();
size = (int)(range & 0xffffffff);
} int start = (int)((range >> ) & 0xffffffff); // calculate the size
int max_size = m_Size - start;
if(size > max_size)
size = max_size; return Range(mp_Buffer + start, size);
} //------------------------------------------------------------------------
// tells the ring buffer how many bytes have been copied to the allocated range
void Add(int size)
{
Lock(); MEMPRO_ASSERT(size >= ); volatile int64 old_range;
int64 new_range; do
{
old_range = GetRangeAtomic(); int64 used_size = (old_range) & 0xffffffff;
used_size += size;
new_range = (old_range & 0xffffffff00000000LL) | used_size; } while(_InterlockedCompareExchange64(&m_UsedRange, new_range, old_range) != old_range); m_BytesAddedEvent.Set(); Release();
} //------------------------------------------------------------------------
// tells the ring buffer how many bytes have been removed from the allocated range
void Remove(int size)
{
Lock(); MEMPRO_ASSERT(size >= ); volatile int64 old_range;
int64 new_range;
int mask = m_Size - ; do
{
old_range = GetRangeAtomic(); int64 used_start = (old_range >> ) & 0xffffffff;
int64 used_size = (old_range) & 0xffffffff;
used_start = (used_start + size) & mask;
used_size -= size;
new_range = (used_start << ) | used_size; } while(_InterlockedCompareExchange64(&m_UsedRange, new_range, old_range) != old_range); m_BytesRemovedEvent.Set(); Release();
} //------------------------------------------------------------------------
int GetUsedBytes() const
{
return (int)(m_UsedRange & 0xffffffff);
} //------------------------------------------------------------------------
void Clear()
{
m_UsedRange = ;
m_BytesRemovedEvent.Reset();
m_BytesAddedEvent.Reset();
} //------------------------------------------------------------------------
// data
private:
int m_Size;
char* mp_Buffer; #ifdef MEMPRO_WIN_BASED_PLATFORM
// NOTE: this MUST be 64bit aligned
__declspec(align()) int64 m_UsedRange; // start index is the high int, size is the low int
#else
int64 m_UsedRange;
#endif #ifdef USE_CRITICAL_SECTIONS
mutable CRITICAL_SECTION m_CriticalSection;
#endif
Event m_BytesRemovedEvent;
Event m_BytesAddedEvent;
};
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_RINGBUFFER_H_INCLUDED //------------------------------------------------------------------------
// Packets.hpp
#ifndef MEMPRO_PACKETS_H_INCLUDED
#define MEMPRO_PACKETS_H_INCLUDED //------------------------------------------------------------------------ //------------------------------------------------------------------------
// MemProMisc.hpp
#ifndef MEMPRO_MEMPROMISC_H_INCLUDED
#define MEMPRO_MEMPROMISC_H_INCLUDED //------------------------------------------------------------------------ #include <stdlib.h> #ifdef MEMPRO_UNIX_BASED_PLATFORM
#include <byteswap.h>
#endif //------------------------------------------------------------------------
// disable some warnings we are not interested in so that we can compile at warning level4
#ifdef MEMPRO_WIN_BASED_PLATFORM
#pragma warning(disable : 4127)
#endif //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#define MEMPRO_SPINLOCK_FREE_VAL 0
#define MEMPRO_SPINLOCK_LOCKED_VAL 1
#define MEMPRO_YIELD_SPIN_COUNT 40
#define MEMPRO_SLEEP_SPIN_COUNT 200 //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
inline int Min(int a, int b) { return a < b ? a : b; } //------------------------------------------------------------------------
inline void SwapEndian(unsigned int& value)
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
value = _byteswap_ulong(value);
#else
value = __bswap_32(value);
#endif
} //------------------------------------------------------------------------
inline void SwapEndian(uint64& value)
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
value = _byteswap_uint64(value);
#else
value = __bswap_64(value);
#endif
} //------------------------------------------------------------------------
inline void SwapEndian(int64& value)
{
SwapEndian((uint64&)value);
} //------------------------------------------------------------------------
template<typename T>
inline void SwapEndian(T& value)
{
MEMPRO_ASSERT(sizeof(T) == sizeof(unsigned int));
SwapEndian((unsigned int&)value);
} //------------------------------------------------------------------------
inline void SwapEndianUInt64Array(void* p, int size)
{
MEMPRO_ASSERT(size % == );
uint64* p_uint64 = (uint64*)p;
uint64* p_end = p_uint64 + size/;
while(p_uint64 != p_end)
SwapEndian(*p_uint64++);
} //------------------------------------------------------------------------
// hi-res timer
#if defined(MEMPRO_PLATFORM_WIN)
inline uint64 GetRDTSC()
{
#ifdef MEMPRO64
return __rdtsc();
#else
__asm
{
; Flush the pipeline
XOR eax, eax
CPUID
; Get RDTSC counter in edx:eax
RDTSC
}
#endif
}
#define GET_CLOCK_COUNT(time) time = GetRDTSC();
#elif defined(MEMPRO_PLATFORM_XBOX360)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_PLATFORM_XBOXONE)
//@Virtuos[wangsongwei]begin:
//#error Please contact slynch@puredevsoftware.com for this platform
inline uint64 GetRDTSC()
{
return __rdtsc();
}
#define GET_CLOCK_COUNT(time) time = GetRDTSC();
//@Virtuos[wangsongwei]end
#endif //------------------------------------------------------------------------
inline int64 GetTime()
{
int64 time; #ifdef MEMPRO_WIN_BASED_PLATFORM
GET_CLOCK_COUNT(time);
#else
timeval curr;
gettimeofday(&curr, NULL);
time = ((int64)curr.tv_sec) * + curr.tv_usec;
#endif
return time;
} //------------------------------------------------------------------------
inline int64 GetTickFrequency()
{
Sleep();
int64 start = GetTime();
Sleep();
int64 end = GetTime();
return end - start;
} //------------------------------------------------------------------------
inline void SetThreadName(unsigned int thread_id, const char* p_name)
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
// see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
const unsigned int MS_VC_EXCEPTION=0x406D1388; struct THREADNAME_INFO
{
unsigned int dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
unsigned int dwThreadID; // Thread ID (-1=caller thread).
unsigned int dwFlags; // Reserved for future use, must be zero.
}; // on the xbox setting thread names messes up the XDK COM API that UnrealConsole uses so check to see if they have been
// explicitly enabled
Sleep();
THREADNAME_INFO ThreadNameInfo;
ThreadNameInfo.dwType = 0x1000;
ThreadNameInfo.szName = p_name;
ThreadNameInfo.dwThreadID = thread_id;
ThreadNameInfo.dwFlags = ; __try
{
RaiseException( MS_VC_EXCEPTION, , sizeof(ThreadNameInfo)/sizeof(ULONG_PTR), (ULONG_PTR*)&ThreadNameInfo );
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
#else
// not supported
#endif
} //------------------------------------------------------------------------
inline void SmallFastMemCpy(void* p_dst, void* p_src, int size)
{
MEMPRO_ASSERT((((size_t)p_dst) & ) == );
MEMPRO_ASSERT((((size_t)p_src) & ) == );
MEMPRO_ASSERT((size & ) == ); unsigned int uint_count = size / sizeof(unsigned int);
unsigned int* p_uint_dst = (unsigned int*)p_dst;
unsigned int* p_uint_src = (unsigned int*)p_src;
for(unsigned int i=; i<uint_count; ++i)
*p_uint_dst++ = *p_uint_src++;
}
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_MEMPROMISC_H_INCLUDED //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
// This file contains all of te packets that can be sent to the MemPro app. //------------------------------------------------------------------------
enum PacketType
{
EInvalid = 0xabcd,
EAllocPacket,
EFreePacket,
ECallstackPacket,
EPageStatePacket,
EPageStateStartPacket, // for backwards compatibility
EPageStateEndPacket_OLD,
EVirtualMemStats,
ETakeSnapshot,
EVMemStats,
EPageStateEndPacket,
EDataStoreEndPacket,
EPulsePacket,
ERequestShutdown
}; //------------------------------------------------------------------------
enum MemProVersion
{
Version =
}; //------------------------------------------------------------------------
enum MemProClientFlags
{
SendPageData = ,
SendPageDataWithMemory,
EShutdownComplete
}; //------------------------------------------------------------------------
// value that is sent immediatley after connection to detect big endian
enum EEndianKey
{
EndianKey = 0xabcdef01
}; //------------------------------------------------------------------------
enum Platform
{
Platform_Windows,
Platform_Unix
}; //------------------------------------------------------------------------
struct PacketHeader
{
PacketType m_PacketType;
int m_Padding;
int64 m_Time; void SwapEndian()
{
MemPro::SwapEndian(m_PacketType);
MemPro::SwapEndian(m_Time);
}
}; //------------------------------------------------------------------------
struct ConnectPacket
{
uint64 m_Padding; // for backwards compatibility int64 m_ConnectTime;
int64 m_TickFrequency; int m_Version;
int m_PtrSize; Platform m_Platform;
int m_Padding2; void SwapEndian()
{
MemPro::SwapEndian(m_Version);
MemPro::SwapEndian(m_ConnectTime);
MemPro::SwapEndian(m_TickFrequency);
MemPro::SwapEndian(m_PtrSize);
}
}; //------------------------------------------------------------------------
struct AllocPacket
{
uint64 m_Addr;
uint64 m_Size;
int m_CallstackID;
int m_Padding; void SwapEndian()
{
MemPro::SwapEndian(m_Addr);
MemPro::SwapEndian(m_Size);
MemPro::SwapEndian(m_CallstackID);
}
}; //------------------------------------------------------------------------
struct FreePacket
{
uint64 m_Addr; void SwapEndian()
{
MemPro::SwapEndian(m_Addr);
}
}; //------------------------------------------------------------------------
struct PageStatePacket
{
uint64 m_Addr;
uint64 m_Size;
PageState m_State;
PageType m_Type;
unsigned int m_Protection;
int m_SendingMemory; void SwapEndian()
{
MemPro::SwapEndian(m_Addr);
MemPro::SwapEndian(m_Size);
MemPro::SwapEndian(m_State);
MemPro::SwapEndian(m_Type);
MemPro::SwapEndian(m_Protection);
MemPro::SwapEndian(m_SendingMemory);
}
}; //------------------------------------------------------------------------
struct VirtualMemStatsPacket
{
uint64 m_Reserved;
uint64 m_Committed; void SwapEndian()
{
MemPro::SwapEndian(m_Reserved);
MemPro::SwapEndian(m_Committed);
}
}; //------------------------------------------------------------------------
struct IgnoreMemRangePacket
{
uint64 m_Addr;
uint64 m_Size; void SwapEndian()
{
MemPro::SwapEndian(m_Addr);
MemPro::SwapEndian(m_Size);
}
};
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_PACKETS_H_INCLUDED //------------------------------------------------------------------------
// Socket.hpp
#ifndef MEMPRO_SOCKET_H_INCLUDED
#define MEMPRO_SOCKET_H_INCLUDED //------------------------------------------------------------------------ //------------------------------------------------------------------------
#if defined(ENABLE_MEMPRO) && !defined(WRITE_DUMP) //------------------------------------------------------------------------
#ifdef MEMPRO_WIN_BASED_PLATFORM
#pragma warning(push)
#pragma warning(disable : 4100)
#endif //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
class SocketImp; //------------------------------------------------------------------------
class Socket
{
public:
inline Socket(); inline ~Socket(); void Disconnect(); bool Bind(const char* p_port); bool StartListening(); bool Accept(Socket& client_socket); int Receive(void* p_buffer, int size); bool Send(void* p_buffer, int size); inline bool IsValid() const { return m_Socket != INVALID_SOCKET; } private:
bool InitialiseWSA(); void CleanupWSA(); void HandleError(); //------------------------------------------------------------------------
// data
SOCKET m_Socket;
}; //------------------------------------------------------------------------
Socket::Socket()
: m_Socket(INVALID_SOCKET)
{
} //------------------------------------------------------------------------
Socket::~Socket()
{
CleanupWSA();
}
} //------------------------------------------------------------------------
#ifdef MEMPRO_WIN_BASED_PLATFORM
#pragma warning(pop)
#endif //------------------------------------------------------------------------
#endif // #if defined(ENABLE_MEMPRO) && !defined(WRITE_DUMP) //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_SOCKET_H_INCLUDED //------------------------------------------------------------------------
// Thread.hpp
#ifndef MEMPRO_THREAD_H_INCLUDED
#define MEMPRO_THREAD_H_INCLUDED //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#ifdef MEMPRO_WIN_BASED_PLATFORM
#pragma warning(push)
#pragma warning(disable : 4100)
#endif //------------------------------------------------------------------------
#ifndef MEMPRO_WIN_BASED_PLATFORM
#include <pthread.h>
#endif //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
typedef int (*ThreadMain)(void*); //------------------------------------------------------------------------
class Thread
{
public:
Thread(); void CreateThread(ThreadMain p_thread_main, void* p_param=NULL); bool IsAlive() const { return m_Alive; } private:
#ifdef MEMPRO_WIN_BASED_PLATFORM
static unsigned long WINAPI PlatformThreadMain(void* p_param);
#else
static void* PlatformThreadMain(void* p_param);
#endif //------------------------------------------------------------------------
// data
private:
#ifdef MEMPRO_WIN_BASED_PLATFORM
mutable HANDLE m_Handle;
#else
mutable pthread_t m_Thread;
#endif
mutable bool m_Alive; mutable ThreadMain mp_ThreadMain;
mutable void* mp_Param;
};
} //------------------------------------------------------------------------
#ifdef MEMPRO_WIN_BASED_PLATFORM
#pragma warning(pop)
#endif //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#endif // #ifndef MEMPRO_THREAD_H_INCLUDED #include <new>
#include <stdio.h>
#include <time.h>
#include <limits.h> #if defined(MEMPRO_PLATFORM_WIN) && !((!defined(MIDL_PASS) && defined(_M_IX86) && !defined(_M_CEE_PURE)) || defined(MemoryBarrier))
#include <atomic>
#endif #ifdef MEMPRO_WIN_BASED_PLATFORM
#include <tchar.h>
#endif //------------------------------------------------------------------------
// disable some warnings we are not interested in so that we can compile at warning level4
#ifdef MEMPRO_WIN_BASED_PLATFORM
#pragma warning(disable : 4127)
#pragma warning(disable : 4100)
#endif //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO //------------------------------------------------------------------------
#if !defined(WRITE_DUMP) && defined(MEMPRO_WIN_BASED_PLATFORM)
#pragma comment(lib, "Ws2_32.lib")
#endif //------------------------------------------------------------------------
// if you are having problems compiling this on your platform undefine ENUMERATE_ALL_MODULES and it send info for just the main module
#ifndef MEMPRO_PLATFORM_XBOXONE
#define ENUMERATE_ALL_MODULES //@Virtuos[wangsongwei] in mempro undef ENUMERATE_ALL_MODULES in xbox one platform
#endif #ifdef ENUMERATE_ALL_MODULES
#ifdef MEMPRO_WIN_BASED_PLATFORM //@Virtuos[wangsongwei] WIN BASED, mean WINDOWS, XBOXONE here #if FRAMEPRO_TOOLSET_UE4
#if PLATFORM_WINDOWS
#include "AllowWindowsPlatformTypes.h"
#endif
#if PLATFORM_XBOXONE
#include "AllowXboxOnePlatformTypes.h"//@Virtuos[wangsongwei] add code support X1 version
#endif
#endif #pragma warning(push)
#pragma warning(disable : 4091)
#include <Dbghelp.h>
#pragma warning(pop) #pragma comment(lib, "Dbghelp.lib") #if FRAMEPRO_TOOLSET_UE4
#if PLATFORM_WINDOWS
#include "HideWindowsPlatformTypes.h"
#endif
#if PLATFORM_XBOXONE
#include "HideXboxOnePlatformTypes.h"//@Virtuos[wangsongwei] add code support X1 version
#endif
#endif
#else
#include <link.h>
#endif
#endif //------------------------------------------------------------------------
#ifdef VMEM_STATS
namespace VMem { void SendStatsToMemPro(void (*send_fn)(void*, int, void*), void* p_context); }
#endif //------------------------------------------------------------------------
//#define TEST_ENDIAN //#define PACKET_START_END_MARKERS #ifdef TEST_ENDIAN
#define ENDIAN_TEST(a) a
#else
#define ENDIAN_TEST(a)
#endif //------------------------------------------------------------------------
// if both of these options are commented out it will use CaptureStackBackTrace (or backtrace on linux)
//#define USE_STACKWALK64 // much slower but possibly more reliable. USE_STACKWALK64 only implemented for x86 builds.
//#define USE_RTLVIRTUALUNWIND // reported to be faster than StackWalk64 - only available on x64 builds
//#define USE_RTLCAPTURESTACKBACKTRACE // system version of USE_RTLVIRTUALUNWIND - only available on x64 builds #if FRAMEPRO_TOOLSET_UE4
#define USE_RTLCAPTURESTACKBACKTRACE
#endif //------------------------------------------------------------------------
#ifdef MEMPRO_WIN_BASED_PLATFORM
#define THREAD_LOCAL_STORAGE __declspec(thread)
#else
#define THREAD_LOCAL_STORAGE __thread
#endif //------------------------------------------------------------------------
namespace MemPro
{
THREAD_LOCAL_STORAGE void** g_CallstackDataTLS = NULL;
} //------------------------------------------------------------------------
#ifdef MEMPRO_PLATFORM_XBOXONE
//#error Please contact slynch@puredevsoftware.com for this platform //@Virtuos[wangsongwei] I don't know what I need to do here, just annotate this line avoid compile error
#endif //------------------------------------------------------------------------
#ifdef USE_RTLCAPTURESTACKBACKTRACE #ifndef MEMPRO64
#error USE_RTLVIRTUALUNWIND only available on x64 builds. Please use a different stack walk function.
#endif //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
void RTLCaptureStackBackTrace(void** stack, int max_stack_size, unsigned int& hash, int& stack_size)
{
memset(stack, , max_stack_size* sizeof(void*));
stack_size = ::RtlCaptureStackBackTrace(,max_stack_size-, stack, (PDWORD)&hash);
stack[stack_size] = ;
}
} #endif // #ifdef USE_RTLCAPTURESTACKBACKTRACE //------------------------------------------------------------------------
#ifdef USE_RTLVIRTUALUNWIND #ifndef MEMPRO64
#error USE_RTLVIRTUALUNWIND only available on x64 builds. Please use a different stack walk function.
#endif namespace MemPro
{
//------------------------------------------------------------------------
__declspec(noinline) VOID VirtualUnwindStackWalk(void** stack, int max_stack_size)
{
CONTEXT context;
memset(&context, , sizeof(context));
RtlCaptureContext(&context); UNWIND_HISTORY_TABLE unwind_history_table;
RtlZeroMemory(&unwind_history_table, sizeof(UNWIND_HISTORY_TABLE)); int frame = ;
for (; frame < max_stack_size-; ++frame)
{
stack[frame] = (void*)context.Rip; ULONG64 image_base;
PRUNTIME_FUNCTION runtime_function = RtlLookupFunctionEntry(context.Rip, &image_base, &unwind_history_table); if (!runtime_function)
{
// If we don't have a RUNTIME_FUNCTION, then we've encountered
// a leaf function. Adjust the stack appropriately.
context.Rip = (ULONG64)(*(PULONG64)context.Rsp);
context.Rsp += ;
}
else
{
// Otherwise, call upon RtlVirtualUnwind to execute the unwind for us.
KNONVOLATILE_CONTEXT_POINTERS nv_context;
RtlZeroMemory(&nv_context, sizeof(KNONVOLATILE_CONTEXT_POINTERS)); PVOID handler_data;
ULONG64 establisher_frame; RtlVirtualUnwind(
/*UNW_FLAG_NHANDLER*/,
image_base,
context.Rip,
runtime_function,
&context,
&handler_data,
&establisher_frame,
&nv_context);
} // If we reach an RIP of zero, this means that we've walked off the end
// of the call stack and are done.
if (!context.Rip)
break;
} stack[frame] = ;
}
}
#endif //------------------------------------------------------------------------
namespace MemPro
{
//------------------------------------------------------------------------
int g_MemProRefs = ; //------------------------------------------------------------------------
const int PAGE_SIZE = ; //------------------------------------------------------------------------
void InitialiseInternal(); //------------------------------------------------------------------------
// MemPro will initialise on the first allocation, but this global ensures
// that MemPro is initialised in the main module. This is sometimes necessary
// if the first allocation comes from a dll.
/*
class Initialiser
{
public:
Initialiser() { MemPro::Initialise(WAIT_FOR_CONNECT); }
} g_Initialiser;
*/ //------------------------------------------------------------------------
// port number
#if defined(MEMPRO_PLATFORM_WIN)
const char* g_DefaultPort = "";
#elif defined(MEMPRO_PLATFORM_XBOX360)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_PLATFORM_XBOXONE)
//@Virtuos[wangsongwei]begin: xbox one use TCP port 4600
//#error Please contact slynch@puredevsoftware.com for this platform
const char* g_DefaultPort = "";
//@Virtuos[wangsongwei]end
#elif defined(MEMPRO_UNIX_BASED_PLATFORM)
const char* g_DefaultPort = "";
#else
#error platform not defined
#endif //------------------------------------------------------------------------
#if defined(MEMPRO_PLATFORM_WIN)
#if (NTDDI_VERSION > NTDDI_WINXP)
#define STACK_TRACE_SIZE 128
#else
#define STACK_TRACE_SIZE 62
#endif
#elif defined(MEMPRO_PLATFORM_XBOX360)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_PLATFORM_XBOXONE)
//@Virtuos[wangsongwei]begin:
//#error Please contact slynch@puredevsoftware.com for this platform
#define STACK_TRACE_SIZE 128 //also use same size for stack trace as windows
//@Virtuos[wangsongwei]end
#elif defined(MEMPRO_UNIX_BASED_PLATFORM)
#define STACK_TRACE_SIZE 128
#else
#error platform not defined
#endif //------------------------------------------------------------------------
// globals
const int g_RingBufferSize = *; #ifdef MEMPRO64
uint64 g_MaxAddr = ULLONG_MAX;
#else
uint64 g_MaxAddr = UINT_MAX;
#endif //------------------------------------------------------------------------
#ifdef WRITE_DUMP
FILE* gp_DumpFile = NULL;
#endif //------------------------------------------------------------------------
uint64 ToUInt64(void* p)
{
#ifdef MEMPRO64
return (uint64)p;
#else
unsigned int u = (unsigned int)p; // cast to uint first to avoid signed bit in casting
return (uint64)u;
#endif
} //------------------------------------------------------------------------
struct DataStorePageHeader
{
int m_Size;
DataStorePageHeader* mp_Next;
}; //------------------------------------------------------------------------
struct CallstackCapture
{
void** mp_Stack;
int m_Size;
unsigned int m_Hash;
}; //------------------------------------------------------------------------
void BaseAddressLookupFunction()
{
} //------------------------------------------------------------------------
class CMemPro
{
public:
CMemPro(); bool Initialise(); void Shutdown(); void Disconnect(bool listen_for_new_connection); void TrackAlloc(void* p, size_t size, bool wait_for_connect); void TrackFree(void* p, bool wait_for_connect); void SendPageState(void* p, size_t size, PageState page_state, PageType page_type, unsigned int page_protection, bool send_memory); void TakeSnapshot(); int SendThreadMain(void* p_param); #ifndef WRITE_DUMP
int ReceiveThreadMain(void* p_param);
#endif int WaitForConnectionThreadMain(void* p_param); void Lock() { m_CriticalSection.Enter(); } void Release() { m_CriticalSection.Leave(); } void WaitForConnectionOnInitialise(); bool IsPaused(); void SetPaused(bool paused); private:
static void GetStackTrace(void** stack, int& stack_size, unsigned int& hash); void SendModuleInfo(); void SendExtraModuleInfo(int64 ModuleBase); void SendString(const char* p_str); #ifdef ENUMERATE_ALL_MODULES
#ifdef MEMPRO_WIN_BASED_PLATFORM
#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
static BOOL CALLBACK EnumerateLoadedModulesCallback(__in PCSTR ModuleName,__in DWORD64 ModuleBase,__in ULONG ModuleSize,__in_opt PVOID UserContext);
#else
static BOOL CALLBACK EnumerateLoadedModulesCallback(__in PCSTR ModuleName,__in ULONG ModuleBase, __in ULONG ModuleSize,__in_opt PVOID UserContext);
#endif
#else
int static EnumerateLoadedModulesCallback(struct dl_phdr_info* info, size_t size, void* data);
#endif
#endif
void StoreData(const void* p_data, int size); void BlockUntilSendThreadEmpty(); void SendStoredData(); void ClearStoreData(); inline bool SendThreadStillAlive() const; void FlushRingBufferForShutdown(); void SendData(const void* p_data, int size); bool SocketSendData(const void* p_data, int size); static void StaticSendVMemStatsData(void* p_data, int size, void* p_context); void SendVMemStatsData(void* p_data, int size); void SendData(unsigned int value); inline void SendPacketHeader(PacketType value); void SendStartMarker(); void SendEndMarker(); inline void Send(bool value); template<typename T> void Send(T& value) { SendData(&value, sizeof(value)); } void Send(unsigned int value) { SendData(value); } void SendPageState(bool send_memory); void SendVMemStats(); void SendVirtualMemStats(); void** AllocateStackTraceData(); CallstackCapture CaptureCallstack(); int SendCallstack(const CallstackCapture& callstack_capture); bool WaitForConnection(); bool WaitForConnectionIfListening(); static int SendThreadMainStatic(void* p_param); static int ReceiveThreadMainStatic(void* p_param); static int WaitForConnectionThreadMainStatic(void* p_param); static int PulseThreadMainStatic(void* p_param); void PulseThreadMain(); void BlockUntilReadyToSend(); //------------------------------------------------------------------------
// data
#ifndef WRITE_DUMP
Socket m_ListenSocket;
Socket m_ClientSocket;
#endif CallstackSet m_CallstackSet; RingBuffer m_RingBuffer;
char m_RingBufferMem[g_RingBufferSize]; volatile bool m_Connected;
volatile bool m_ReadyToSend; volatile bool m_InEvent; volatile bool m_Paused; Event m_StartedListeningEvent;
Event m_WaitForConnectThreadFinishedEvent;
Event m_SendThreadFinishedEvent;
Event m_ReceiveThreadFinishedEvent;
Event m_MemProReadyToShutdownEvent;
Event m_PulseThreadFinished; volatile bool m_StartedListening;
volatile bool m_InitialConnectionTimedOut; int m_LastPageStateSend;
int m_PageStateInterval; int m_LastVMemStatsSend;
int m_VMemStatsSendInterval; bool m_WaitForConnect; static const int m_DataStorePageSize = ;
DataStorePageHeader* mp_DataStoreHead; // used to store allocs before initialised
DataStorePageHeader* mp_DataStoreTail; Thread m_SendThread;
Thread m_ReceiveThread;
Thread m_PulseThread;
Thread m_WaitForConnectionThread; bool m_FlushedRingBufferForShutdown; CriticalSection m_CriticalSection;
CriticalSection m_DisconnectCriticalSection; int m_ModulesSent; volatile bool m_ShuttingDown; BlockAllocator<Allocator> m_BlockAllocator;
}; //------------------------------------------------------------------------
char g_MemProMem[sizeof(CMemPro)];
CMemPro* gp_MemPro = NULL;
volatile bool g_ShuttingDown = false; //------------------------------------------------------------------------
inline CMemPro* GetMemPro()
{
if(!gp_MemPro)
InitialiseInternal(); return gp_MemPro;
} //------------------------------------------------------------------------
CMemPro::CMemPro()
: m_RingBuffer(m_RingBufferMem, g_RingBufferSize),
m_Connected(false),
m_ReadyToSend(false),
m_InEvent(false),
m_Paused(false),
m_StartedListeningEvent(false, false),
m_WaitForConnectThreadFinishedEvent(false, false),
m_SendThreadFinishedEvent(true, false),
m_ReceiveThreadFinishedEvent(true, false),
m_MemProReadyToShutdownEvent(false, false),
m_PulseThreadFinished(true, false),
m_StartedListening(false),
m_InitialConnectionTimedOut(false),
m_LastPageStateSend(),
m_PageStateInterval(),
m_LastVMemStatsSend(),
m_VMemStatsSendInterval(),
m_WaitForConnect(false),
mp_DataStoreHead(NULL),
mp_DataStoreTail(NULL),
m_FlushedRingBufferForShutdown(false),
m_ModulesSent(),
m_ShuttingDown(false)
{
} //------------------------------------------------------------------------
inline unsigned int GetHash(void** p_stack, int stack_size)
{
#ifdef MEMPRO64
const unsigned int prime = 0x01000193;
unsigned int hash = prime;
void** p = p_stack;
for(int i=; i<stack_size; ++i)
{
uint64 key = ToUInt64(*p++);
key = (~key) + (key << );
key = key ^ (key >> );
key = key * ;
key = key ^ (key >> );
key = key + (key << );
key = key ^ (key >> );
hash = hash ^ (unsigned int)key;
} return hash;
#else
const unsigned int prime = 0x01000193;
unsigned int hash = prime;
for(int i=; i<stack_size; ++i)
hash = (hash * prime) ^ (unsigned int)p_stack[i]; return hash;
#endif
} //------------------------------------------------------------------------
inline unsigned int GetHashAndStackSize(void** p_stack, int& stack_size)
{
#ifdef MEMPRO64
const unsigned int prime = 0x01000193;
unsigned int hash = prime;
stack_size = ;
void** p = p_stack;
while(*p)
{
uint64 key = ToUInt64(*p++);
key = (~key) + (key << );
key = key ^ (key >> );
key = key * ;
key = key ^ (key >> );
key = key + (key << );
key = key ^ (key >> );
hash = hash ^ (unsigned int)key;
++stack_size;
} return hash;
#else
const unsigned int prime = 0x01000193;
unsigned int hash = prime;
stack_size = ;
while(p_stack[stack_size])
{
hash = (hash * prime) ^ (unsigned int)p_stack[stack_size];
++stack_size;
} return hash;
#endif
} //------------------------------------------------------------------------
void CMemPro::GetStackTrace(void** stack, int& stack_size, unsigned int& hash)
{
#if defined(MEMPRO_PLATFORM_WIN)
#if defined(USE_STACKWALK64) #ifdef MEMPRO64
#error USE_STACKWALK64 only works in x86 builds. Please use a different stack walk funtion.
#endif // get the context
CONTEXT context;
memset(&context, , sizeof(context));
RtlCaptureContext(&context); // setup the stack frame
STACKFRAME64 stack_frame;
memset(&stack_frame, , sizeof(stack_frame));
stack_frame.AddrPC.Mode = AddrModeFlat;
stack_frame.AddrFrame.Mode = AddrModeFlat;
stack_frame.AddrStack.Mode = AddrModeFlat;
#ifdef MEMPRO64
DWORD machine = IMAGE_FILE_MACHINE_IA64;
stack_frame.AddrPC.Offset = context.Rip;
stack_frame.AddrFrame.Offset = context.Rsp;
stack_frame.AddrStack.Offset = context.Rbp;
#else
DWORD machine = IMAGE_FILE_MACHINE_I386;
stack_frame.AddrPC.Offset = context.Eip;
stack_frame.AddrFrame.Offset = context.Ebp;
stack_frame.AddrStack.Offset = context.Esp;
#endif
HANDLE thread = GetCurrentThread(); static HANDLE process = GetCurrentProcess(); stack_size = ;
while(StackWalk64(
machine,
process,
thread,
&stack_frame,
&context,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL) && stack_size < STACK_TRACE_SIZE)
{
void* p = (void*)(stack_frame.AddrPC.Offset);
stack[stack_size++] = p;
}
hash = GetHash(stack, stack_size);
#elif defined(USE_RTLVIRTUALUNWIND)
MemPro::VirtualUnwindStackWalk(stack, STACK_TRACE_SIZE);
hash = GetHashAndStackSize(stack, stack_size);
#elif defined(USE_RTLCAPTURESTACKBACKTRACE)
MemPro::RTLCaptureStackBackTrace(stack, STACK_TRACE_SIZE, hash, stack_size);
#else
CaptureStackBackTrace(, STACK_TRACE_SIZE, stack, (PDWORD)&hash);
for(stack_size = ; stack_size<STACK_TRACE_SIZE; ++stack_size)
if(!stack[stack_size])
break;
#endif
#elif defined(MEMPRO_PLATFORM_XBOX360)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_PLATFORM_XBOXONE)
//@Virtuos[wangsongwei]begin: refer to XboxOneStackWalk.cpp
//#error Please contact slynch@puredevsoftware.com for this platform
MemPro::RTLCaptureStackBackTrace(stack, STACK_TRACE_SIZE, hash, stack_size); //in fact, in UE4 all platform should use this capture stack trace function
//@Virtuos[wangsongwei]end
#elif defined(MEMPRO_UNIX_BASED_PLATFORM)
stack_size = backtrace(stack, STACK_TRACE_SIZE);
hash = GetHashAndStackSize(stack, stack_size);
#else
#error platform not defined
#endif
} //------------------------------------------------------------------------
void CMemPro::StaticSendVMemStatsData(void* p_data, int size, void* p_context)
{
CMemPro* p_this = (CMemPro*)p_context;
p_this->SendVMemStatsData(p_data, size);
} //------------------------------------------------------------------------
void CMemPro::SendVMemStatsData(void* p_data, int size)
{
static char buffer[];
MEMPRO_ASSERT(size <= (int)sizeof(buffer));
memcpy_s(buffer, sizeof(buffer), p_data, size);
ENDIAN_TEST(SwapEndianUInt64Array(buffer, size));
SendData(buffer, size);
} //------------------------------------------------------------------------
void CMemPro::StoreData(const void* p_data, int size)
{
MEMPRO_ASSERT(size < m_DataStorePageSize - (int)sizeof(DataStorePageHeader)); if(!mp_DataStoreTail || mp_DataStoreTail->m_Size + size > m_DataStorePageSize)
{
DataStorePageHeader* p_new_page = (DataStorePageHeader*)Allocator::Alloc(m_DataStorePageSize);
p_new_page->m_Size = sizeof(DataStorePageHeader);
p_new_page->mp_Next = NULL; if(mp_DataStoreTail)
mp_DataStoreTail->mp_Next = p_new_page;
else
mp_DataStoreHead = p_new_page; mp_DataStoreTail = p_new_page;
} memcpy((char*)mp_DataStoreTail + mp_DataStoreTail->m_Size, p_data, size);
mp_DataStoreTail->m_Size += size;
} //------------------------------------------------------------------------
void CMemPro::BlockUntilSendThreadEmpty()
{
// wait for the send thread to have sent all of the stored data
while(m_Connected && m_RingBuffer.GetAllocatedRange().m_Size)
Sleep();
} //------------------------------------------------------------------------
void CMemPro::SendStoredData()
{
if(!m_Connected)
return; DataStorePageHeader* p_page = mp_DataStoreHead; if(p_page)
{
while(p_page)
{
DataStorePageHeader* p_next = p_page->mp_Next; SendData((char*)p_page + sizeof(DataStorePageHeader), p_page->m_Size - sizeof(DataStorePageHeader));
Allocator::Free(p_page, m_DataStorePageSize); p_page = p_next;
} SendPacketHeader(EDataStoreEndPacket);
SendEndMarker();
} #ifndef WRITE_DUMP
BlockUntilSendThreadEmpty();
#endif mp_DataStoreHead = mp_DataStoreTail = NULL;
} //------------------------------------------------------------------------
void CMemPro::ClearStoreData()
{
DataStorePageHeader* p_page = mp_DataStoreHead;
while(p_page)
{
DataStorePageHeader* p_next = p_page->mp_Next;
Allocator::Free(p_page, m_DataStorePageSize);
p_page = p_next;
} mp_DataStoreHead = mp_DataStoreTail = NULL; m_CallstackSet.Clear();
} //------------------------------------------------------------------------
void CMemPro::Send(bool value)
{
unsigned int uint_value = value ? : ;
Send(uint_value);
} //------------------------------------------------------------------------
bool CMemPro::SendThreadStillAlive() const
{
return m_SendThread.IsAlive();
} //------------------------------------------------------------------------
void CMemPro::FlushRingBufferForShutdown()
{
if(m_FlushedRingBufferForShutdown)
return;
m_FlushedRingBufferForShutdown = true; RingBuffer::Range range = m_RingBuffer.GetAllocatedRange();
while(range.m_Size)
{
SocketSendData(range.mp_Buffer, range.m_Size);
range = m_RingBuffer.GetAllocatedRange();
}
} //------------------------------------------------------------------------
void CMemPro::SendData(const void* p_data, int size)
{
MEMPRO_ASSERT((size & ) == ); if(!m_Connected)
{
StoreData(p_data, size);
return;
} if(!SendThreadStillAlive())
{
FlushRingBufferForShutdown();
SocketSendData(p_data, size);
}
else
{
int bytes_to_copy = size;
char* p_src = (char*)p_data;
while(bytes_to_copy)
{
RingBuffer::Range range;
do {
range = m_RingBuffer.GetFreeRange();
if(!m_Connected)
return;
} while(!range.m_Size);
if(!m_Connected)
return; int copy_size = Min(range.m_Size, bytes_to_copy);
SmallFastMemCpy(range.mp_Buffer, p_src, copy_size);
p_src += copy_size;
bytes_to_copy -= copy_size; m_RingBuffer.Add(copy_size);
}
}
} //------------------------------------------------------------------------
// slightly more optimal version for sending a single uint. Because all ringbuffer
// operations are 4 byte aligned we can be guaranteed that the uint won't be split
// between the end and start of the buffer, we will always get it in one piece.
void CMemPro::SendData(unsigned int value)
{
if(!m_Connected)
{
StoreData(&value, sizeof(value));
return;
} if(!SendThreadStillAlive())
{
FlushRingBufferForShutdown();
SocketSendData(&value, sizeof(value));
#ifdef WRITE_DUMP
fflush(gp_DumpFile);
#endif
}
else
{
RingBuffer::Range range;
do {
range = m_RingBuffer.GetFreeRange();
if(!m_Connected)
return;
} while(!range.m_Size);
if(!m_Connected)
return; MEMPRO_ASSERT(range.m_Size >= (int)sizeof(unsigned int));
MEMPRO_ASSERT((((size_t)range.mp_Buffer) & ) == );
*(unsigned int*)range.mp_Buffer = value; m_RingBuffer.Add(sizeof(value));
}
} //------------------------------------------------------------------------
void CMemPro::SendPacketHeader(PacketType value)
{
SendStartMarker(); PacketHeader header;
header.m_PacketType = value;
header.m_Time = GetTime(); Send(header);
} //------------------------------------------------------------------------
void CMemPro::SendStartMarker()
{
#ifdef PACKET_START_END_MARKERS
unsigned int start_marker = 0xabcdef01;
ENDIAN_TEST(SwapEndian(start_marker));
Send(start_marker);
#endif
} //------------------------------------------------------------------------
void CMemPro::SendEndMarker()
{
#ifdef PACKET_START_END_MARKERS
unsigned int end_marker = 0xaabbccdd;
ENDIAN_TEST(SwapEndian(end_marker));
Send(end_marker);
#endif
} //------------------------------------------------------------------------
void CMemPro::SendPageState(bool send_memory)
{
CriticalSectionScope lock(m_CriticalSection); SendPacketHeader(EPageStateStartPacket);
SendEndMarker(); #ifdef MEMPRO_WIN_BASED_PLATFORM
MEMORY_BASIC_INFORMATION info;
memset(&info, , sizeof(info)); uint64 addr = ; HANDLE process = GetCurrentProcess(); bool found_page = false; while(addr < g_MaxAddr)
{
uint64 last_addr = addr; if(VirtualQueryEx(process, (void*)addr, &info, sizeof(info)) != )
{
if((info.State == MEM_RESERVE || info.State == MEM_COMMIT) && info.Protect != PAGE_NOACCESS)
{
PageState page_state;
switch(info.State)
{
case MEM_RESERVE: page_state = MemPro::Reserved; break;
case MEM_COMMIT: page_state = MemPro::Committed; break;
default: page_state = MemPro::Committed; MEMPRO_ASSERT(false); break;
} #if defined(MEMPRO_PLATFORM_WIN)
PageType page_type;
switch(info.Type)
{
case MEM_IMAGE: page_type = page_Image; break;
case MEM_MAPPED: page_type = page_Mapped; break;
case MEM_PRIVATE: page_type = page_Private; break;
default: page_type = page_Unknown; break;
}
#elif defined(MEMPRO_PLATFORM_XBOX360)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_PLATFORM_XBOXONE)
//@Virtuos[wangsongwei]begin: xbox one should use same PageType
//#error Please contact slynch@puredevsoftware.com for this platform
PageType page_type;
switch(info.Type)
{
case MEM_IMAGE: page_type = page_Image; break;
case MEM_MAPPED: page_type = page_Mapped; break;
case MEM_PRIVATE: page_type = page_Private; break;
default: page_type = page_Unknown; break;
}
//@Virtuos[wangsongwei]end
#else
#error platform not defined
#endif
SendPageState(info.BaseAddress, info.RegionSize, page_state, page_type, info.Protect, send_memory);
} addr += info.RegionSize;
found_page = true;
}
else
{
if(!found_page)
addr += PAGE_SIZE;
else
break; // VirtualQueryEx should only fail when it gets to the end, assuming it has found at least one page
} if(addr < last_addr) // handle wrap around
break;
}
#endif
SendPacketHeader(EPageStateEndPacket); IgnoreMemRangePacket range_packet;
range_packet.m_Addr = ToUInt64(m_RingBufferMem);
range_packet.m_Size = sizeof(m_RingBufferMem);
Send(range_packet); SendEndMarker();
} //------------------------------------------------------------------------
void CMemPro::SendVMemStats()
{
#ifdef VMEM_STATS
//Send(EVMemStats);//@Virtuos[wangsongwei] error ? should use SendPacketHeader ?
SendPacketHeader(EVMemStats);//@Virtuos[wangsongwei] test dingyifei code int64 time = GetTime();
Send(time); VMem::SendStatsToMemPro(StaticSendVMemStatsData, this);
#endif
} //------------------------------------------------------------------------
void CMemPro::SendVirtualMemStats()
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
MEMORY_BASIC_INFORMATION info;
memset(&info, , sizeof(info)); uint64 addr = ;
size_t reserved = ;
size_t committed = ; HANDLE process = GetCurrentProcess(); bool started = false; while(addr < g_MaxAddr)
{
uint64 last_addr = addr; if(VirtualQueryEx(process, (void*)addr, &info, sizeof(info)) != )
{
switch(info.State)
{
case MEM_RESERVE: reserved += info.RegionSize; break;
case MEM_COMMIT: committed += info.RegionSize; break;
} addr += info.RegionSize; started = true;
}
else
{
if(started)
break; addr = (addr & (~((size_t)PAGE_SIZE-))) + PAGE_SIZE;
} if(addr < last_addr) // handle wrap around
break;
} #ifdef MEMPRO_PLATFORM_XBOX360
#error Please contact slynch@puredevsoftware.com for this platform
#endif
reserved += committed; SendPacketHeader(EVirtualMemStats); VirtualMemStatsPacket packet;
packet.m_Reserved = reserved;
packet.m_Committed = committed;
ENDIAN_TEST(packet.SwapEndian());
Send(packet);
#else
SendPacketHeader(EVirtualMemStats); VirtualMemStatsPacket packet;
packet.m_Reserved = ;
packet.m_Committed = ;
ENDIAN_TEST(packet.SwapEndian());
Send(packet);
#endif
SendEndMarker();
} //------------------------------------------------------------------------
void** CMemPro::AllocateStackTraceData()
{
CriticalSectionScope lock(m_CriticalSection);
return (void**)m_BlockAllocator.Alloc(STACK_TRACE_SIZE*sizeof(void*));
} //------------------------------------------------------------------------
CallstackCapture CMemPro::CaptureCallstack()
{
CallstackCapture callstack; callstack.mp_Stack = g_CallstackDataTLS;
if(!callstack.mp_Stack)
{
callstack.mp_Stack = AllocateStackTraceData();
g_CallstackDataTLS = callstack.mp_Stack;
} callstack.m_Hash = ;
callstack.m_Size = ;
GetStackTrace(callstack.mp_Stack, callstack.m_Size, callstack.m_Hash); #ifdef USE_RTLVIRTUALUNWIND
const int ignore_count = ;
#else
const int ignore_count = ;
#endif
callstack.m_Size -= ignore_count;
if(callstack.m_Size <= )
{
callstack.mp_Stack[] = (void*)-;
callstack.m_Size = ;
} return callstack;
} //------------------------------------------------------------------------
int CMemPro::SendCallstack(const CallstackCapture& callstack_capture)
{
void** p_stack = callstack_capture.mp_Stack;
int stack_size = callstack_capture.m_Size;
int hash = callstack_capture.m_Hash; #ifdef MEMPRO64
uint64* stack64 = (uint64*)p_stack;
#else
static uint64 stack64_static[STACK_TRACE_SIZE];
for(int i=; i<stack_size; ++i)
stack64_static[i] = ToUInt64(p_stack[i]);
uint64* stack64 = stack64_static;
#endif Callstack* p_callstack = m_CallstackSet.Get(stack64, stack_size, hash); if(!p_callstack)
{
p_callstack = m_CallstackSet.Add(stack64, stack_size, hash); SendPacketHeader(ECallstackPacket); int callstack_id = p_callstack->m_ID;
#ifdef TEST_ENDIAN
SwapEndian(callstack_id);
#endif
Send(callstack_id); int send_stack_size = stack_size;
#ifdef TEST_ENDIAN
for(int i=; i<stack_size; ++i) SwapEndian(stack64[i]);
SwapEndian(send_stack_size);
#endif
Send(send_stack_size);
SendData(stack64, stack_size*sizeof(uint64)); SendEndMarker();
} return p_callstack->m_ID;
} //------------------------------------------------------------------------
void CMemPro::TakeSnapshot()
{
CriticalSectionScope lock(m_CriticalSection);
SendPacketHeader(ETakeSnapshot);
} //------------------------------------------------------------------------
int CMemPro::SendThreadMainStatic(void* p_param)
{
return gp_MemPro->SendThreadMain(p_param);
} //------------------------------------------------------------------------
bool CMemPro::SocketSendData(const void* p_data, int size)
{
#ifdef WRITE_DUMP
MEMPRO_ASSERT(gp_DumpFile);
size_t result = fwrite(p_data, size, , gp_DumpFile);
MEMPRO_ASSERT(result == );
return true;
#else
return m_ClientSocket.Send((void*)p_data, size);
#endif
} //------------------------------------------------------------------------
int CMemPro::SendThreadMain(void* p_param)
{
while(m_Connected)
{
RingBuffer::Range range;
do {
range = m_RingBuffer.GetAllocatedRange(); // timeout: check for disconnect every 100 ms
if(!m_Connected)
{
m_SendThreadFinishedEvent.Set();
return ;
}
} while(!range.m_Size); if(!SocketSendData(range.mp_Buffer, range.m_Size))
{
m_SendThreadFinishedEvent.Set();
Disconnect(true);
return ;
} m_RingBuffer.Remove(range.m_Size);
} m_SendThreadFinishedEvent.Set();
return ;
} //------------------------------------------------------------------------
#ifndef WRITE_DUMP
int CMemPro::ReceiveThreadMainStatic(void* p_param)
{
return gp_MemPro->ReceiveThreadMain(p_param);
}
#endif //------------------------------------------------------------------------
#ifndef WRITE_DUMP
int CMemPro::ReceiveThreadMain(void* p_param)
{
while(m_Connected)
{
unsigned int flag = ; if(m_ClientSocket.Receive(&flag, sizeof(flag)) != sizeof(flag))
{
m_ReceiveThreadFinishedEvent.Set();
Disconnect(true);
return ;
} switch(flag)
{
case SendPageData: SendPageState(false/*send_memory*/); break;
case SendPageDataWithMemory: SendPageState(true/*send memory*/); break;
case EShutdownComplete: m_MemProReadyToShutdownEvent.Set(); break;
}
} m_ReceiveThreadFinishedEvent.Set();
return ;
}
#endif //------------------------------------------------------------------------
// http://www.debuginfo.com/articles/debuginfomatch.html #ifdef MEMPRO_WIN_BASED_PLATFORM
struct CV_HEADER
{
int Signature;
int Offset;
}; struct CV_INFO_PDB20
{
CV_HEADER CvHeader;
int Signature;
int Age;
char PdbFileName[MAX_PATH];
}; struct CV_INFO_PDB70
{
int CvSignature;
GUID Signature;
int Age;
char PdbFileName[MAX_PATH];
};
#endif void CMemPro::SendExtraModuleInfo(int64 ModuleBase)
{
#ifdef MEMPRO_PLATFORM_WIN
IMAGE_DOS_HEADER* p_dos_header = (IMAGE_DOS_HEADER*)ModuleBase;
IMAGE_NT_HEADERS* p_nt_header = (IMAGE_NT_HEADERS*)((char*)ModuleBase + p_dos_header->e_lfanew);
IMAGE_OPTIONAL_HEADER& optional_header = p_nt_header->OptionalHeader;
IMAGE_DATA_DIRECTORY& image_data_directory = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
IMAGE_DEBUG_DIRECTORY* p_debug_info_array = (IMAGE_DEBUG_DIRECTORY*)(ModuleBase + image_data_directory.VirtualAddress);
int count = image_data_directory.Size / sizeof(IMAGE_DEBUG_DIRECTORY);
for(int i=; i<count; ++i)
{
if(p_debug_info_array[i].Type == IMAGE_DEBUG_TYPE_CODEVIEW)
{
char* p_cv_data = (char*)(ModuleBase + p_debug_info_array[i].AddressOfRawData);
if(strncmp(p_cv_data, "RSDS", ) == )
{
CV_INFO_PDB70* p_cv_info = (CV_INFO_PDB70*)p_cv_data;
Send(true); // sending info
Send(p_cv_info->Age);
Send(p_cv_info->Signature);
SendString(p_cv_info->PdbFileName);
return; // returning here
}
else if(strncmp(p_cv_data, "NB10", ) == )
{
Send(true); // sending info
CV_INFO_PDB20* p_cv_info = (CV_INFO_PDB20*)p_cv_data;
Send(p_cv_info->Age);
Send(p_cv_info->Signature);
SendString(p_cv_info->PdbFileName);
return; // returning here
}
}
}
#endif
// failed to find info
Send(false); // not sending info
} //------------------------------------------------------------------------
void CMemPro::SendString(const char* p_str)
{
const int max_path_len = ;
int len = (int)strlen(p_str) + ;
MEMPRO_ASSERT(len <= max_path_len); // round up to 4 bytes
static char temp[max_path_len];
memset(temp, , sizeof(temp));
memcpy(temp, p_str, len); int rounded_len = ((int)len + ) & ~;
Send(rounded_len); SendData(temp, rounded_len);
} //------------------------------------------------------------------------
#ifdef ENUMERATE_ALL_MODULES
#ifdef MEMPRO_WIN_BASED_PLATFORM
#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64)
// depending on your platform you may need to change PCSTR to PSTR for ModuleName
BOOL CALLBACK CMemPro::EnumerateLoadedModulesCallback(__in PCSTR ModuleName,__in DWORD64 ModuleBase,__in ULONG ModuleSize,__in_opt PVOID UserContext)
#else
BOOL CALLBACK CMemPro::EnumerateLoadedModulesCallback(__in PCSTR ModuleName,__in ULONG ModuleBase,__in ULONG ModuleSize,__in_opt PVOID UserContext)
#endif
{
CMemPro* p_this = (CMemPro*)UserContext; int64 module_base = ModuleBase;
p_this->Send(module_base); p_this->SendString(ModuleName); p_this->SendExtraModuleInfo(ModuleBase); ++p_this->m_ModulesSent; return true;
}
#else
int CMemPro::EnumerateLoadedModulesCallback(struct dl_phdr_info* info, size_t size, void* data)
{
CMemPro* p_this = (CMemPro*)data; int64 module_base = ;
for (int j = ; j < info->dlpi_phnum; j++)
{
if (info->dlpi_phdr[j].p_type == PT_LOAD)
{
module_base = info->dlpi_addr + info->dlpi_phdr[j].p_vaddr;
break;
}
} if(p_this->m_ModulesSent == )
{
// send the module base address
int64 lookup_fn_marker = 0xabcdefabcdef1LL;
p_this->Send(lookup_fn_marker); int64 module_base = (int64)BaseAddressLookupFunction; // use the address of the BaseAddressLookupFunction function so that we can work it out later
p_this->Send(module_base); // get the module name
char arg1[];
char char_filename[MAX_PATH];
sprintf(arg1, "/proc/%d/exe", getpid());
memset(char_filename, , MAX_PATH);
readlink(arg1, char_filename, MAX_PATH-);
p_this->SendString(char_filename);
}
else
{
p_this->Send(module_base);
p_this->SendString(info->dlpi_name);
} p_this->SendExtraModuleInfo(); ++p_this->m_ModulesSent; return ;
}
#endif
#endif //------------------------------------------------------------------------
void CMemPro::SendModuleInfo()
{
Send(true); // indicate we are going to be sending module signatures - for backwards compatibility
uint64 extra_module_info = 0xabcdef;
Send(extra_module_info); m_ModulesSent = ; // if you are having problems compiling this on your platform undefine ENUMERATE_ALL_MODULES and it send info for just the main module
#ifdef ENUMERATE_ALL_MODULES
#ifdef MEMPRO_WIN_BASED_PLATFORM
#ifdef MEMPRO64
EnumerateLoadedModules64(GetCurrentProcess(), EnumerateLoadedModulesCallback, this);
#else
EnumerateLoadedModules(GetCurrentProcess(), EnumerateLoadedModulesCallback, this);
#endif
#else
dl_iterate_phdr(EnumerateLoadedModulesCallback, this);
#endif
#endif // if ENUMERATE_ALL_MODULES is disabled or enumeration failed for some reason, fall back
// to getting the base address for the main module. This will always for for all platforms.
if(m_ModulesSent == )
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
static int module = ;
HMODULE module_handle = ;
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)&module, &module_handle); int64 module_base = (int64)module_handle;
Send(module_base); TCHAR tchar_filename[MAX_PATH] = { };
GetModuleFileName(NULL, tchar_filename, MAX_PATH); char char_filename[MAX_PATH]; #ifdef UNICODE
size_t chars_converted = ;
wcstombs_s(&chars_converted, char_filename, tchar_filename, MAX_PATH);
#else
strcpy_s(char_filename, tchar_filename);
#endif SendString(char_filename); Send(false); // not sending SendExtraModuleInfo
#else
// let MemPro know we are sending the lookup function address, not the base address
uint64 use_module_base_addr_marker = 0xabcdefabcdef1LL;
Send(use_module_base_addr_marker); // send the module base address
int64 module_base = (int64)BaseAddressLookupFunction; // use the address of the BaseAddressLookupFunction function so that we can work it out later
Send(module_base); // send the module name
char char_filename[MAX_PATH]; // get the module name
char arg1[];
sprintf(arg1, "/proc/%d/exe", getpid());
memset(char_filename, , MAX_PATH);
readlink(arg1, char_filename, MAX_PATH-); SendString(char_filename); Send(false); // not sending SendExtraModuleInfo
#endif
} uint64 terminator = ;
Send(terminator);
} //------------------------------------------------------------------------
bool CMemPro::WaitForConnection()
{
m_CriticalSection.Enter(); #ifdef WRITE_DUMP
OutputDebugString(_T("MemPro writing to dump file " WRITE_DUMP _T("\n")));
#ifdef MEMPRO_WIN_BASED_PLATFORM
_tfopen_s(&gp_DumpFile, WRITE_DUMP, _T("wb"));
#else
gp_DumpFile = fopen(WRITE_DUMP, _T("wb"));
#endif
MEMPRO_ASSERT(gp_DumpFile); m_Connected = true; m_SendThreadFinishedEvent.Reset(); // start the sending thread
int thread_id = ;
m_SendThread.CreateThread(SendThreadMainStatic, &thread_id);
SetThreadName(thread_id, "MemPro write thread");
#else
// start listening for connections
if(m_ListenSocket.IsValid() && !m_ListenSocket.StartListening())
{
m_WaitForConnectThreadFinishedEvent.Set(); // do this before Shutdown
Shutdown();
m_CriticalSection.Leave();
return false;
} m_StartedListening = true;
m_StartedListeningEvent.Set(); // Accept a client socket
bool accepted = false;
if(m_ListenSocket.IsValid())
{
m_CriticalSection.Leave();
accepted = m_ListenSocket.Accept(m_ClientSocket); if(!accepted)
{
bool shutting_down = m_ShuttingDown;
m_WaitForConnectThreadFinishedEvent.Set(); // do this before Shutdown
if(!shutting_down) // check shutting down here in case CMemPro has been destructed
{
m_CriticalSection.Enter();
Shutdown();
m_CriticalSection.Leave();
}
return false;
}
} m_CriticalSection.Enter(); m_Connected = true; m_SendThreadFinishedEvent.Reset();
m_ReceiveThreadFinishedEvent.Reset(); // start the sending thread
int send_thread_id = ;
m_SendThread.CreateThread(SendThreadMainStatic, &send_thread_id);
SetThreadName(send_thread_id, "MemPro send thread"); // start the receiving thread
int receive_thread_id = ;
m_ReceiveThread.CreateThread(ReceiveThreadMainStatic, &receive_thread_id);
SetThreadName(receive_thread_id, "MemPro receive thread");
#endif
// send the connect key
unsigned int endian_key = (unsigned int)EndianKey;
ENDIAN_TEST(SwapEndian(endian_key));
Send(endian_key); // send the connect packet
ConnectPacket connect_packet;
connect_packet.m_Padding = 0xabcdabcd;
connect_packet.m_Version = MemPro::Version;
connect_packet.m_TickFrequency = GetTickFrequency();
connect_packet.m_ConnectTime = GetTime(); connect_packet.m_PtrSize = sizeof(void*); #ifdef MEMPRO_WIN_BASED_PLATFORM
connect_packet.m_Platform = Platform_Windows;
#else
connect_packet.m_Platform = Platform_Unix;
#endif ENDIAN_TEST(connect_packet.SwapEndian());
Send(connect_packet); SendModuleInfo(); #if defined(MEMPRO_PLATFORM_WIN)
#if (!defined(MIDL_PASS) && defined(_M_IX86) && !defined(_M_CEE_PURE)) || defined(MemoryBarrier)
MemoryBarrier();
#else
std::atomic_thread_fence(std::memory_order_seq_cst);
#endif
#elif defined(MEMPRO_PLATFORM_XBOX360)
#error Please contact slynch@puredevsoftware.com for this platform
#elif defined(MEMPRO_PLATFORM_XBOXONE)
//@Virtuos[wangsongwei]begin:
//#error Please contact slynch@puredevsoftware.com for this platform
// #if (!defined(MIDL_PASS) && defined(_M_IX86) && !defined(_M_CEE_PURE)) || defined(MemoryBarrier)
//MemoryBarrier();
FPlatformMisc::MemoryBarrier();//UE4's barrier
// #else
// std::atomic_thread_fence(std::memory_order_seq_cst);
// #endif
//@Virtuos[wangsongwei]end
#elif defined(MEMPRO_UNIX_BASED_PLATFORM)
__sync_synchronize();
#else
#error platform not defined
#endif SendStoredData(); m_ReadyToSend = true; m_WaitForConnectThreadFinishedEvent.Set();
m_CriticalSection.Leave(); // start the pulse thread
int pulse_thread_id = ;
m_PulseThreadFinished.Reset();
m_PulseThread.CreateThread(PulseThreadMainStatic, &pulse_thread_id);
SetThreadName(pulse_thread_id, "MemPro pulse thread"); return true;
} //------------------------------------------------------------------------
int CMemPro::WaitForConnectionThreadMainStatic(void* p_param)
{
return gp_MemPro->WaitForConnectionThreadMain(p_param);
} //------------------------------------------------------------------------
int CMemPro::PulseThreadMainStatic(void* p_param)
{
gp_MemPro->PulseThreadMain();
return ;
} //------------------------------------------------------------------------
void CMemPro::PulseThreadMain()
{
while(m_Connected)
{
{
CriticalSectionScope lock(m_CriticalSection);
if(!m_Connected)
break; SendPacketHeader(EPulsePacket);
SendEndMarker();
} Sleep();
} m_PulseThreadFinished.Set();
} //------------------------------------------------------------------------
int CMemPro::WaitForConnectionThreadMain(void* p_param)
{
#ifdef WRITE_DUMP
Sleep(MEMPRO_INIT_DELAY);
#else
if(!m_ListenSocket.IsValid())
{
Sleep(MEMPRO_INIT_DELAY); bool bind_result = m_ListenSocket.Bind(g_DefaultPort); if(!bind_result)
OutputDebugString(_T("MemPro ERROR: Failed to bind port. This usually means that another process is already running with MemPro enabled.\n"));
MEMPRO_ASSERT(bind_result);
if(!bind_result)
return ;
}
#endif
WaitForConnection(); return ;
} //------------------------------------------------------------------------
bool CMemPro::Initialise()
{
m_WaitForConnectionThread.CreateThread(WaitForConnectionThreadMainStatic, NULL); return true;
} //------------------------------------------------------------------------
void CMemPro::Shutdown()
{
m_ShuttingDown = true; // wait for MemPro to have handled all data
if(m_SendThread.IsAlive())
{
SendPacketHeader(ERequestShutdown);
SendEndMarker();
m_MemProReadyToShutdownEvent.Wait( * ); // do this so that we don't start listening after the listen socket has been shutdown and deadlock
m_CriticalSection.Leave();
m_StartedListeningEvent.Wait();
m_CriticalSection.Enter(); if(m_WaitForConnect)
{
BlockUntilReadyToSend();
BlockUntilSendThreadEmpty();
}
} Disconnect(false/*listen_for_new_connection*/); m_CriticalSection.Leave();
m_PulseThreadFinished.Wait();
m_CriticalSection.Enter(); #ifndef WRITE_DUMP
m_ListenSocket.Disconnect(); if(m_WaitForConnectionThread.IsAlive())
m_WaitForConnectThreadFinishedEvent.Wait(); #ifdef MEMPRO_WIN_BASED_PLATFORM
WSACleanup();
#endif
#endif
} //------------------------------------------------------------------------
void CMemPro::Disconnect(bool listen_for_new_connection)
{
CriticalSectionScope lock(m_DisconnectCriticalSection); if(m_Connected)
{
m_ReadyToSend = false;
m_Connected = false; // wait for the send thread to shutdown
m_SendThreadFinishedEvent.Wait();
m_SendThreadFinishedEvent.Reset(); #ifdef WRITE_DUMP
fclose(gp_DumpFile);
gp_DumpFile = NULL;
#else
// close the client socket
m_ClientSocket.Disconnect(); // wait for the receive thread to shutdown
m_ReceiveThreadFinishedEvent.Wait();
m_ReceiveThreadFinishedEvent.Reset();
#endif
// clear stuff
m_CallstackSet.Clear(); m_RingBuffer.Clear(); #ifndef WRITE_DUMP
if(listen_for_new_connection)
{
CriticalSectionScope lock2(m_CriticalSection); // start listening for another connection
m_ListenSocket.Disconnect();
m_StartedListeningEvent.Reset();
m_StartedListening = false;
m_InitialConnectionTimedOut = false;
m_WaitForConnectionThread.CreateThread(WaitForConnectionThreadMainStatic, NULL);
}
#endif
}
} //------------------------------------------------------------------------
void CMemPro::BlockUntilReadyToSend()
{
#ifndef WRITE_DUMP
if(m_ListenSocket.IsValid())
{
OutputDebugString(_T("Waiting for connection to MemPro...\n")); int64 start_time = GetTime();
while(!m_ReadyToSend && m_ListenSocket.IsValid() &&
(m_WaitForConnect || ((GetTime() - start_time) / (double)GetTickFrequency()) * < MEMPRO_CONNECT_TIMEOUT))
{
m_CriticalSection.Leave();
Sleep();
m_CriticalSection.Enter();
} if(m_ReadyToSend)
{
OutputDebugString(_T("Connected to MemPro!\n"));
}
else
{
m_InitialConnectionTimedOut = true;
ClearStoreData();
OutputDebugString(_T("Failed to connect to MemPro\n"));
}
}
#endif
} //------------------------------------------------------------------------
// return true to continue processing event (either connected or before started listening)
bool CMemPro::WaitForConnectionIfListening()
{
#ifdef WRITE_DUMP
return true;
#else
if(!m_ReadyToSend && !m_InitialConnectionTimedOut)
{
CriticalSectionScope lock(m_CriticalSection); // store data until we have started listening
if(!m_StartedListening)
return true; BlockUntilReadyToSend();
} return m_ReadyToSend;
#endif
} //------------------------------------------------------------------------
void CMemPro::TrackAlloc(void* p, size_t size, bool wait_for_connect)
{
if(m_Paused)
return; m_WaitForConnect = wait_for_connect; if(!WaitForConnectionIfListening())
return; CallstackCapture callstack_capture = CaptureCallstack(); CriticalSectionScope lock(m_CriticalSection); #ifndef WRITE_DUMP
#ifdef MEMPRO_WIN_BASED_PLATFORM
if(m_ListenSocket.IsValid())
{
int now = GetTickCount();
if(now - m_LastPageStateSend > m_PageStateInterval)
{
SendVirtualMemStats();
m_LastPageStateSend = now;
} if(now - m_LastVMemStatsSend > m_VMemStatsSendInterval)
{
SendVMemStats();
m_LastVMemStatsSend = now;
}
}
#endif
#endif
if(m_InEvent)
return;
m_InEvent = true; int callstack_id = SendCallstack(callstack_capture); SendPacketHeader(EAllocPacket); AllocPacket packet;
packet.m_Addr = ToUInt64(p);
packet.m_Size = size;
packet.m_CallstackID = callstack_id;
packet.m_Padding = 0xef12ef12;
ENDIAN_TEST(packet.SwapEndian());
Send(packet); SendEndMarker(); m_InEvent = false;
} //------------------------------------------------------------------------
void CMemPro::TrackFree(void* p, bool wait_for_connect)
{
if(m_Paused)
return; m_WaitForConnect = wait_for_connect; if(!WaitForConnectionIfListening())
return; CriticalSectionScope lock(m_CriticalSection); if(m_InEvent)
return;
m_InEvent = true; SendPacketHeader(EFreePacket); FreePacket packet;
packet.m_Addr = ToUInt64(p);
ENDIAN_TEST(packet.SwapEndian());
Send(packet); SendEndMarker(); m_InEvent = false;
} //------------------------------------------------------------------------
bool CMemPro::IsPaused()
{
return m_Paused;
} //------------------------------------------------------------------------
void CMemPro::SetPaused(bool paused)
{
m_Paused = paused;
} //------------------------------------------------------------------------
void CMemPro::SendPageState(void* p, size_t size, PageState page_state, PageType page_type, unsigned int page_protection, bool send_memory)
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
if(!WaitForConnectionIfListening())
return; SendPacketHeader(EPageStatePacket); bool send_page_mem = send_memory && page_state == Committed && (page_protection & (PAGE_NOACCESS | PAGE_EXECUTE | PAGE_GUARD)) == ; PageStatePacket packet;
packet.m_Addr = ToUInt64(p);
packet.m_Size = size;
packet.m_State = page_state;
packet.m_Type = page_type;
packet.m_Protection = page_protection;
packet.m_SendingMemory = send_page_mem;
ENDIAN_TEST(packet.SwapEndian());
Send(packet); if(send_page_mem)
{
MEMPRO_ASSERT(!(size % PAGE_SIZE));
char* p_page = (char*)p;
char* p_end_page = p_page + size;
while(p_page != p_end_page)
{
SendData(p_page, PAGE_SIZE);
p_page += PAGE_SIZE;
}
} SendEndMarker();
#endif
} //------------------------------------------------------------------------
void CMemPro::WaitForConnectionOnInitialise()
{
m_WaitForConnect = true; m_StartedListeningEvent.Wait(); CriticalSectionScope lock(m_CriticalSection);
BlockUntilReadyToSend();
}
} //------------------------------------------------------------------------
void MemPro::InitialiseInternal()
{
if(!gp_MemPro && !g_ShuttingDown)
{
gp_MemPro = (CMemPro*)g_MemProMem;
new (gp_MemPro)CMemPro();
gp_MemPro->Initialise();
}
} //------------------------------------------------------------------------
void MemPro::IncRef()
{
++g_MemProRefs;
} //------------------------------------------------------------------------
void MemPro::DecRef()
{
if(--g_MemProRefs == )
Shutdown();
} //------------------------------------------------------------------------
// called by the APP (not internally)
void MemPro::Initialise(bool wait_for_connect)
{
InitialiseInternal(); if(wait_for_connect)
gp_MemPro->WaitForConnectionOnInitialise();
} //------------------------------------------------------------------------
void MemPro::Disconnect()
{
if(gp_MemPro)
{
gp_MemPro->Lock();
gp_MemPro->Disconnect(true);
gp_MemPro->Release();
}
} //------------------------------------------------------------------------
void MemPro::Shutdown()
{
if(!g_ShuttingDown)
{
g_ShuttingDown = true;
if(gp_MemPro)
{
gp_MemPro->Lock();
gp_MemPro->Shutdown();
gp_MemPro->Release();
gp_MemPro->~CMemPro();
memset(gp_MemPro, , sizeof(CMemPro));
gp_MemPro = NULL;
}
}
} //------------------------------------------------------------------------
void MemPro::TrackAlloc(void* p, size_t size, bool wait_for_connect)
{
CMemPro* p_mempro = GetMemPro();
if(p_mempro)
p_mempro->TrackAlloc(p, size, wait_for_connect);
} //------------------------------------------------------------------------
void MemPro::TrackFree(void* p, bool wait_for_connect)
{
CMemPro* p_mempro = GetMemPro();
if(p_mempro)
p_mempro->TrackFree(p, wait_for_connect);
} //------------------------------------------------------------------------
void MemPro::SetPaused(bool paused)
{
CMemPro* p_mempro = GetMemPro();
if(p_mempro)
p_mempro->SetPaused(paused);
} //------------------------------------------------------------------------
bool MemPro::IsPaused()
{
CMemPro* p_mempro = GetMemPro();
return p_mempro ? p_mempro->IsPaused() : false;
} //------------------------------------------------------------------------
void MemPro::SendPageState(void* p, size_t size, PageState page_state, PageType page_type, unsigned int page_protection, bool send_memory)
{
CMemPro* p_mempro = GetMemPro();
if(p_mempro)
p_mempro->SendPageState(p, size, page_state, page_type, page_protection, send_memory);
} //------------------------------------------------------------------------
void MemPro::TakeSnapshot()
{
if(gp_MemPro) gp_MemPro->TakeSnapshot();
} //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPRO
//------------------------------------------------------------------------
// MemProLib.cpp //------------------------------------------------------------------------
namespace MemPro
{
int mempro_total_alloc = ; //------------------------------------------------------------------------
#if defined(MEMPRO_PLATFORM_XBOXONE)
//#error Please contact slynch@puredevsoftware.com for this platform //@Virtuos[wangsongwei] I don't know what I need to do here, just annotate this line avoid compile error
#endif
}
//------------------------------------------------------------------------
// Socket.cpp #include <stdlib.h>
#include <new> #ifdef MEMPRO_WIN_BASED_PLATFORM
#include <tchar.h>
#endif //------------------------------------------------------------------------
#if defined(ENABLE_MEMPRO) && !defined(WRITE_DUMP) //------------------------------------------------------------------------
namespace MemPro
{
volatile int g_InitialiseCount = ;
} //------------------------------------------------------------------------
bool MemPro::Socket::InitialiseWSA()
{
if(g_InitialiseCount == )
{
#ifdef MEMPRO_PLATFORM_XBOX360
#error Please contact slynch@puredevsoftware.com for this platform
#endif #ifdef MEMPRO_WIN_BASED_PLATFORM
// Initialize Winsock
WSADATA wsaData;
if(WSAStartup(MAKEWORD(,), &wsaData) != )
{
HandleError();
return false;
}
#endif
} ++g_InitialiseCount; return true;
} //------------------------------------------------------------------------
void MemPro::Socket::CleanupWSA()
{
--g_InitialiseCount; if(g_InitialiseCount == )
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
if(WSACleanup() == SOCKET_ERROR)
HandleError();
#endif #ifdef MEMPRO_PLATFORM_XBOX360
#error Please contact slynch@puredevsoftware.com for this platform
#endif
}
} //------------------------------------------------------------------------
void MemPro::Socket::Disconnect()
{
if(m_Socket != INVALID_SOCKET)
{
#ifdef MEMPRO_WIN_BASED_PLATFORM
if(shutdown(m_Socket, SD_BOTH) == SOCKET_ERROR)
HandleError();
#else
if(shutdown(m_Socket, SHUT_RDWR) == SOCKET_ERROR)
HandleError();
#endif // loop until the socket is closed to ensure all data is sent
unsigned int buffer = ;
size_t ret = ;
do { ret = recv(m_Socket, (char*)&buffer, sizeof(buffer), ); } while(ret != && ret != (size_t)SOCKET_ERROR); #ifdef MEMPRO_WIN_BASED_PLATFORM
if(closesocket(m_Socket) == SOCKET_ERROR)
HandleError();
#else
close(m_Socket);
#endif
m_Socket = INVALID_SOCKET;
}
} //------------------------------------------------------------------------
bool MemPro::Socket::StartListening()
{
MEMPRO_ASSERT(m_Socket != INVALID_SOCKET); if (listen(m_Socket, SOMAXCONN) == SOCKET_ERROR)
{
HandleError();
return false;
}
return true;
} //------------------------------------------------------------------------
bool MemPro::Socket::Bind(const char* p_port)
{
MEMPRO_ASSERT(m_Socket == INVALID_SOCKET); if(!InitialiseWSA())
return false; #ifdef MEMPRO_PLATFORM_WIN
// setup the addrinfo struct
addrinfo info;
ZeroMemory(&info, sizeof(info));
info.ai_family = AF_INET;
info.ai_socktype = SOCK_STREAM;
info.ai_protocol = IPPROTO_TCP;
info.ai_flags = AI_PASSIVE; // Resolve the server address and port
addrinfo* p_result_info;
HRESULT result = getaddrinfo(NULL, p_port, &info, &p_result_info);
if (result != )
{
HandleError();
return false;
} m_Socket = socket(
p_result_info->ai_family,
p_result_info->ai_socktype,
p_result_info->ai_protocol);
#else
m_Socket = socket(
AF_INET,
SOCK_STREAM,
IPPROTO_TCP);
#endif if (m_Socket == INVALID_SOCKET)
{
#ifdef MEMPRO_PLATFORM_WIN
freeaddrinfo(p_result_info);
#endif
HandleError();
return false;
} // Setup the TCP listening socket
#ifdef MEMPRO_PLATFORM_WIN
result = ::bind(m_Socket, p_result_info->ai_addr, (int)p_result_info->ai_addrlen);
freeaddrinfo(p_result_info);
#else
// Bind to INADDR_ANY
SOCKADDR_IN sa;
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = INADDR_ANY;
int iport = atoi(p_port);
sa.sin_port = htons(iport);
int result = ::bind(m_Socket, (const sockaddr*)(&sa), sizeof(SOCKADDR_IN));
#endif if (result == SOCKET_ERROR)
{
HandleError();
Disconnect();
return false;
} return true;
} //------------------------------------------------------------------------
bool MemPro::Socket::Accept(Socket& client_socket)
{
MEMPRO_ASSERT(client_socket.m_Socket == INVALID_SOCKET);
client_socket.m_Socket = accept(m_Socket, NULL, NULL);
return client_socket.m_Socket != INVALID_SOCKET;
} //------------------------------------------------------------------------
bool MemPro::Socket::Send(void* p_buffer, int size)
{
int bytes_to_send = size;
while(bytes_to_send != )
{
int bytes_sent = (int)send(m_Socket, (char*)p_buffer, bytes_to_send, );
if(bytes_sent == SOCKET_ERROR)
{
HandleError();
Disconnect();
return false;
}
p_buffer = (char*)p_buffer + bytes_sent;
bytes_to_send -= bytes_sent;
} return true;
} //------------------------------------------------------------------------
int MemPro::Socket::Receive(void* p_buffer, int size)
{
int total_bytes_received = ;
while(size)
{
int bytes_received = (int)recv(m_Socket, (char*)p_buffer, size, ); total_bytes_received += bytes_received; if(bytes_received == )
{
Disconnect();
return bytes_received;
}
else if(bytes_received == SOCKET_ERROR)
{
HandleError();
Disconnect();
return bytes_received;
} size -= bytes_received;
p_buffer = (char*)p_buffer + bytes_received;
} return total_bytes_received;
} //------------------------------------------------------------------------
void MemPro::Socket::HandleError()
{
#ifdef MEMPRO_PLATFORM_WIN
if(WSAGetLastError() == WSAEADDRINUSE)
{
OutputDebugString(_T("MemPro: Network connection conflict. Please make sure that other MemPro enabled applications are shut down, or change the port in the the MemPro lib and MemPro settings.\n"));
return;
} TCHAR* p_buffer = NULL;
va_list args;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
WSAGetLastError(),
,
(TCHAR*)&p_buffer,
* ,
&args); OutputDebugString(p_buffer); LocalFree(p_buffer);
#endif
} //------------------------------------------------------------------------
#endif // #if defined(ENABLE_MEMPRO) && !defined(WRITE_DUMP)
//------------------------------------------------------------------------
// Thread.cpp //------------------------------------------------------------------------
#ifdef ENABLE_MEMPRO #ifdef MEMPRO_UNIX_BASED_PLATFORM
#include <pthread.h>
#endif //------------------------------------------------------------------------
MemPro::Thread::Thread()
#ifdef MEMPRO_WIN_BASED_PLATFORM
: m_Handle(),
m_Alive(false)
#else
: m_Alive(false)
#endif
{
} //------------------------------------------------------------------------
void MemPro::Thread::CreateThread(ThreadMain p_thread_main, void* p_param)
{
mp_ThreadMain = p_thread_main;
mp_Param = p_param; #ifdef MEMPRO_WIN_BASED_PLATFORM
m_Handle = ::CreateThread(NULL, , PlatformThreadMain, this, , NULL);
#else
pthread_create(&m_Thread, NULL, PlatformThreadMain, this);
#endif
} //------------------------------------------------------------------------
#ifdef MEMPRO_WIN_BASED_PLATFORM
unsigned long WINAPI MemPro::Thread::PlatformThreadMain(void* p_param)
{
Thread* p_thread = (Thread*)p_param;
p_thread->m_Alive = true;
unsigned long ret = (unsigned long)p_thread->mp_ThreadMain(p_thread->mp_Param);
p_thread->m_Alive = false;
return ret;
}
#else
void* MemPro::Thread::PlatformThreadMain(void* p_param)
{
Thread* p_thread = (Thread*)p_param;
p_thread->m_Alive = true;
p_thread->mp_ThreadMain(p_thread->mp_Param);
p_thread->m_Alive = false;
return NULL;
}
#endif //------------------------------------------------------------------------
#endif // #ifdef ENABLE_MEMPROMemPro.cpp
1. 将以上的hpp文件放到UE4\Engine\Source\Runtime\Core\Public\HAL,cpp文件放到UE4\Engine\Source\Runtime\Core\Private\HAL;
2. 文件Build.h末尾加上:
#if (!UE_EDITOR) && (PLATFORM_WINDOWS || PLATFORM_XBOXONE)
#define ENABLE_VIRTUOS_MEMPRO_UE4 //comment it to disable MemPro
#endif
3. 文件MallocBinned.h 加上(windows 和X1都走这个开辟文件):
#ifdef ENABLE_VIRTUOS_MEMPRO_UE4
#include "MemPro.hpp"
#endif virtual void* Malloc( SIZE_T Size, uint32 Alignment ) override
{
... ...
#ifdef ENABLE_VIRTUOS_MEMPRO_UE4
MEMPRO_TRACK_ALLOC(Free, Size);
#endif return Free;
} virtual bool Free( void* Ptr ) override
{
if( !Ptr )
{
return true;
} #ifdef ENABLE_VIRTUOS_MEMPRO_UE4
MEMPRO_TRACK_FREE(Ptr);
#endif return PushFreeLockless(Ptr);
}
4. 如果要disable MemPro就在Build.h文件中注释掉即可;
运行方法:
当进行完以上四步后,怎样运行MemPro,X1和Windows是不同的:
------------------------------------------------------------------------------------------------
Windows版本exe利用MemPro运行:
1. 利用TCP socket传输dump数据,实时在MemPro的GUI中进行查看分析:
打开MemPro在其GUI界面中设置好相关属性,其中包括Launch的exe路径(带".exe"),Working Dir(不带exe文件,只是路径,用于搜索与exe同名的pdb文件),启动游戏带的命令行参数(非必需),socket的ip和端口可以保持默认,默认的为(在Setting栏下查看):
Connect IP:localhost
Connect Port:27016
Secondary Port:1255
所有的设置都会保存在: C:\Users\username\AppData\Roaming\MemPro\MemPro.settings文件里面;
然后在MemPro的GUI中点击Launch即可,其会利用Launch按钮中设定的exe和命令参数启动游戏;启动后可以“MemorySnapshot”按钮抓取dump,“Leak”分析内存泄露;
注意,如果在MemPro正在监视你的游戏的时候关掉游戏窗口,Mempro就会自动抓取最后一个dump,在这个dump中,本来是一定泄露(“Leaked Memory”未引用的内存,未被释放)就会成为可以的泄露“Suspected memory leaked”;应该是因为进程被关掉了,那么之前的一定泄露的内存也就被系统回收了;
2. 让游戏运行过程中写出dump文件,然后关掉游戏在MemPro中分析dump:
此种方法需要在MemPro.hpp文件中打开以下代码:
//------------------------------------------------------------------------
#define WRITE_DUMP _T("allocs.mempro_dump")//@Virtuos[wangsongwei] enable this macro to write dump instead of pass dump data by TCP socket
默认生成的dump文件是allocs.mempro_dump, 路径在exe旁边(附近层级文件夹里面);可以通过“MemPro.exe allocs.mempro_dump”解析这个dump文件;解析前理论上应该在MemPro中设置对应的exe和working dir路径才对,这样解析的时候才能找到正确的pdb,但是发现没设置也行,估计是pdb信息都已经在解析的时候不需要了,在dump里了;
------------------------------------------------------------------------------------------------
X1版本exe利用MemPro运行:
由于MemPro本身无法远程启动X1 kit上的游戏(目前我还没确定方法),现在研究出来的X1版本的游戏和MemPro结合使用有两种方法:
1. 先启动X1上游戏,然后利用MemPro 的Connect按钮Connect上去进行解析;
先进行相关设置:设置Launch和Connect:
注意这里的ip,由于一个X1的kit开发机是有两个IP的,比如这里我的是有Tools IP是192.254.xxx.xxx,Console IP是10.0.x.xxx,这里使用的是console IP;我们团队这个IP基本不会改变,反而Tools IP经常改动,估计是IT干的事情导致的,所以用console IP以后可以不用频繁改动了;至于端口号4600是X1为TCP传输预留的端口,必须使用这个,UDP是4601;另外注意的是,我本机测试时候通过Tools IP是无法进行连接的,必须通过Console IP才能连接得上;
配置完成,运行游戏,可以通过VS启动kit上游戏,或者利用X1的"XboxOneManager"工具都可以,等游戏在kit上启动起来,点击MemPro的Connect按钮,就可以连接上kit运行的游戏,接下来进行截取dump和分析leak都和windows平台的的一样,mempro代码会通过socket TCP传输dump但本机PC,另外解析会很慢;
在本地测试中,利用MemPro的"Subtract Snapshots"功能可以生成新的dump,但是在leaks解析的时候Mempro会crash,可能游戏太大导致dump太大,也可能是我的代码还有点问题;
此种方法最大的弊端是,游戏已经在kit上运行起来了,中途connect到kit上去,得到的数据都不是游戏从零启动的所有内存状态,对于随后的解析而言是不准确的;而MemPro的“MemoryGraph”里面显示的也只是自从connect上去之后的内存开辟情况,可以发现很少,因为connect之后可能开辟内存的行为就很少,而如果能够记录出游戏刚刚启动时候的所有开辟,就会是一个很大的量;因为这种泄露解析算法必须是要求所有dump数据都有,利用增量的方式来进行解析;但是对于一些streaming前后的比较,认为这种方式可能还是有点用的;
2. 离线使用MemPro,让X1游戏写入dump到kit上,而不是利用socket传输到本机PC,然后在本机上从kit上拷贝出X1的dump,利用MemPro解析这个dump:
此种方法是官方给出的如果无法使用socket实现TCP连接的情况下的备选方案;它的好处应该是可以让游戏从头开始跑的时候就开始记录数据,这就解决了上面中途connect上去,丢失游戏刚启动的时候的内存开辟数据无法截取到的问题;
关于从X1 kit上拷贝下来文件,简单的方法可以利用XBoxOne Manager来查看文件夹目录;或者利用命令行命令:
xbapp list:列出connect的kit上的所有app;
xbdir {MyTitle_1.0.0.0_neutral__8wekyb3d8bbwe}:\ 或者
xbdir /X:/title XG:\ 来在文件夹层级之间切换
xbcp xbcp source [target] [/A[[:]attributes]] [/B] [/S[[:]levels]] [/X[:]address [+<sessionkey>][/title]] 用于拷贝文件到本机
3. 在本机利用批处理文件启动MemPro的同时启动X1上的游戏,MemPro
思路是,现在MemPro中配置好相关的设置,这些设置会存储在C:\Users\username\AppData\Roaming\MemPro\MemPro.settings,理想情况下,我们利用某个命令行参数指定给MemPro.exe就应该可以完成按照这个配置文件connect对应的exe,然后我们利用X1的XDK中自带xbapp.exe在本机远程启动kit上的游戏,由于二者行为几乎没有多少时间差,这样就几乎在kit上游戏启动时候就connect上去了,就几乎能够抓到游戏启动开始后的所有的内存开辟行为;
相关的xbapp指令有:
@echo off
set IP=
set /p IP=Please input the IP address of the xboxone you want to connect:
set path=%DurangoXDK%\bin\
xbconnect %IP%
pause @echo off
Echo Need connect to XboxOne first, then run this bat!!!
set path=%DurangoXDK%\bin\
xbapp.exe deploy "New folder (2)" /v
pause @echo off
Echo Need connect to XboxOne first, then run this bat!!!
set path=%DurangoXDK%\bin\
xbapp.exe launch xxx_xxxxxxxxxxxx!yyyy -LoadSavedGame=C1_006.bsg
pause
rem xxxxx is your game id, yyyy is your game name without ".exe"
实验发现,此思路不可行,主要是没找到MemPro.exe可以跟哪个参数直接通过命令行进行connect到某个exe或者launch某个exe,可能本来就没这个功能;此方案暂时流产;
===============================================================================================================================================================
如果在VS中运行时候挂在以下代码里面(这种情况点击“Launch”也会挂掉):
int CMemPro::WaitForConnectionThreadMain(void* p_param)
{
#ifdef WRITE_DUMP
Sleep(MEMPRO_INIT_DELAY);
#else
if(!m_ListenSocket.IsValid())
{
Sleep(MEMPRO_INIT_DELAY); bool bind_result = m_ListenSocket.Bind(g_DefaultPort); if(!bind_result)
OutputDebugString(_T("MemPro ERROR: Failed to bind port. This usually means that another process is already running with MemPro enabled.\n"));
MEMPRO_ASSERT(bind_result);
if(!bind_result)
return ;
}
#endif
WaitForConnection(); return ;
}
按照提示是因为已经有了一个mempro在运行,这时候在任务管理器里面kill掉process “cmd. exe*32”;当开始运行mempro的时候会自动启动,如果上次没有正常关闭,该进程不会自动结束,导致下次使用mempro crash(点击“Launch”按钮crash多半是这个原因);
================================================================================================================================================================
其他:
如果在Unreal里面包含windows的头文件:
Where we need access to the Windows types in engine, we wrap the include in:
#include "AllowWindowsPlatformTypes.h"
#include "MyWindowsStuff.h"
#include "HideWindowsPlatformTypes.h"
-------------------------------------------------------------------------------------------------------
如上文中的代码:
void *malloc(int size)
{
void* (*ptr)(int);
void* handle = (void*)-;
ptr = (void*)dlsym(handle, "malloc");
if(!ptr) abort();
void *p = (*ptr)(size);
MEMPRO_TRACK_ALLOC(p, size);
return p;
}
其中dlsym根据动态链接库操作句柄与符号,返回符号对应的地址。使用这个函数不但可以获取函数地址,也可以获取变量地址。这里获取系统的malloc函数指针,然后利用函数地址调用它,这样的方法确保调用的malloc确实是系统的,而不是被别人重载的;
-------------------------------------------------------------------------------------------------------
《Note --- Unreal --- MemPro (CONTINUE... ...)》的更多相关文章
-
智捷公开课马上开始了-欢迎大家一起讨论学习-第一系列读《Swift开发指南(修订版) 》看Swift视频教程
引用: 智捷课堂携手51CTO学院.图灵教育联合举办iOS线上培训就业班系列体验公开课. 分享移动开发.移动设计方向最新,最热,最抢眼技术热点以及设计经验.我们每周将最少举办一次公开课,同时会提前安排 ...
-
201871010101-陈来弟《面相对象程序设计(java)》第十周学习总结
201871010101-陈来弟<面相对象程序设计(java)>第十周学习总结 实验八异常.断言与日志 实验时间 2019-11-1 1.实验目的与要求 (1) 掌握java异常处理技术: ...
-
《阿里巴巴编码规范(JAVA)》认证考后感
2018.02.15除夕拿下了阿里云认证的<阿里巴巴编码规范(JAVA)>认证,写下这篇考后感,记录考试中碰到的一些考点. 先总体介绍下这个考试规则,50道选择题,大部分是多选题,有少部分 ...
-
《玩转Bootstrap(基础)》笔记
基本的HTML模板 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...
-
C++学习书籍推荐《C++标准库(第一版)》下载
百度云及其他网盘下载地址:点我 编辑推荐 <C++标准程序库:自修教程与参考手册>编辑推荐:C++标准程序库提供了一组通用类别(classes)和界面(interfaes),可大幅扩充C+ ...
-
201771010135 杨蓉庆《面对对象程序设计(java)》第十八周学习总结
1.实验目的与要求 (1) 综合掌握java基本程序结构: (2) 综合掌握java面向对象程序设计特点: (3) 综合掌握java GUI 程序设计结构: (4) 综合掌握java多线程编程模型: ...
-
201771010128王玉兰《面象对象程序设计(Java)》第七周学习总结
第一部分:基础知识总结: 1继承 A:用已有类来构建新类的一种机制,当定义了一个新类继承一个类时,这个新类就继承了这个类的方法和域以适应新的情况: B:特点:具有层次结构.子类继承父类的方法和域: C ...
-
《Java应用程序(Application)》
在编写Java应用程序(Application)时可以这样: 1,定义包名. 2, 导入相关的包. 3, 定义一个类. 4,定义相关变量. 5,定义构造函数.(在构造函数内调用init()方法和add ...
-
《JAVA学习笔记(1---13-4)》
[1]问题: 1.什么叫做面向过程? 2.什么叫做面向对象? 解答: 1: 所谓的面向过程就是我们是一个执行者,我们要开发一个项目,这个项目要求要实现很多功能,作为执行者的我们就需要 去一个一个的找这 ...
随机推荐
-
【大结局】《从案例中学习JavaScript》之酷炫音乐播放器(四)
这是之前写的用H5制作的音乐播放器,前三节其实已经做得差不多了,音轨的制作原理已经在上一节说明,不过一直还没有和音乐对接. 本章作为该系列的一个完结篇,我会专门把动态音轨的实现代码贴出来,demo地址 ...
-
mac 安装命令行开发者工具
terminal中执行: xcode-select --install然后点击 “安装”即可 mac命令行工具
-
JAVA GUI随笔
Java的布局管理器提供了一种层面的抽象,自动将用户界面映射到所有的窗口系统.GUI组件放置在容器中,它们的位置由容器的布局管理器来管理. 布局管理器是使用布局管理器类创建的. 注:同一个对象无论添加 ...
-
Chrome Restful Api 测试工具 Postman-REST-Client离线安装包下载,Axure RP Extension for Chrome离线版下载
[Postman for Chrome 离线下载] Postman-REST-Client离线安装包,可直接在Chrome浏览器本地安装使用,可模拟各种http请求,Restful Api测试, CS ...
-
Redis安装教程
1. Linux下Redis安装教程 (1)安装 #tar xf redis-2.6.14.tar.gz #cd redis-2.6.14 #make #make install (2)配置 修改re ...
-
linux下安装cmake和mysql遇到的问题总结
首先是在安装cmake的过程中遇到的问题: 1.開始使用yum命令安装时,不知道为什么一直不行,然后就准备wget 来先下载压缩包,再手动编译. 因为网络限制,wget不能下载外网的东西一直显示con ...
-
js数组、对象、正则
1.根据给定的条件在原有的数组上,得到所需要的新数组var a = [-1, -1, 1, 2, -2, -2, -3, -3, 3, -3];function f(s, e) { var re ...
-
实验吧_简单的sql注入_1、2、3
简单的sql注入1 看着这个简单的界面,一时间没有特别好的思路,先输入一个1',发生了报错 初步猜测这是一个字符型的注入,他将我们输入的语句直接当成sql语句执行了,按题目的意思后面肯定过滤了很多注入 ...
-
Java面试题之多线程打印
概述 作为程序员经常在面试的时候遇到多线程的问题,我印象比较深刻的就是下面这道题:写两个线程,一个线程打印 1~52,另一个线程打印字母A-Z.打印顺序为12A34B56C……5152Z.看这个题目已 ...
-
laravel实战化项目之三板斧
laravel实战化项目之三板斧 spring mvc 实战化项目之三板斧 asp.net mvc 实战化项目之三板斧 laravel是我工作10多年来见到的真正能称得上让phper从面条一样杂乱的代 ...