You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/CryEngine/CrySystem/MemoryFragmentationProfiler.h

326 lines
9.9 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#ifndef CRYINCLUDE_CRYSYSTEM_MEMORYFRAGMENTATIONPROFILER_H
#define CRYINCLUDE_CRYSYSTEM_MEMORYFRAGMENTATIONPROFILER_H
#pragma once
// useful class to investigate memory fragmentation
// every time you call this from the console:
//
// #System.DumpMemoryCoverage()
//
// it adds a line to "MemoryCoverage.bmp" (generated the first time, there is a max line count)
// blue stripes mark some special positions (DLL positions)
// Dependencies: only CryLog()
#include <vector> // STL vector<>
#if defined(WIN32) || defined(WIN64)
class CMemoryFragmentationProfiler
{
public:
// constructor - clean file
CMemoryFragmentationProfiler()
: m_dwLine(0xffffffff) // 0xffffffff means not initialized yet
{
}
// call this if you want to add one line (on first call the file is generated)
void DumpMemoryCoverage()
{
if (m_dwLine == 0xffffffff)
{
Init();
}
const size_t nMinMemoryPerUnit = 4 * 1024; // down to a few KB
const size_t nUnitsPerLine = 1024 * 8; // amount of bits, should only occupy a few KB memory
const size_t nMaxMemoryPerUnit = 0x100000000 / nUnitsPerLine; // 4GB in total
static std::vector<bool> vCoverage;
vCoverage.clear();
vCoverage.resize(nUnitsPerLine, 0); // should occupy nUnitsPerLine/8 bytes (vector<bool> is specialized)
size_t nAvailableMem = 0, nUsedMem = 0;
const size_t nMallocOverhead = 24; // depends on used runtime (debug:32, release:24)
size_t nCurrentUnitSize = 256 * 1024 * 1024; // start with 256 MB blocks
void** pMemoryBlocks = 0; // linked list of memory blocks (to free them)
size_t nUnits = 0;
uint32 dwAllocCnt = 0, dwFreeCnt = 0;
while (nCurrentUnitSize >= nMinMemoryPerUnit)
{
size_t nLocalUnits = 0;
for (;; )
{
void** pMem = (void**)::malloc(nCurrentUnitSize - nMallocOverhead);
if (!pMem)
{
break;
}
++dwAllocCnt;
// update coverage (conservative)
{
size_t nStartUnit = ((size_t)pMem + nMaxMemoryPerUnit - 1) / (nMaxMemoryPerUnit);
size_t nEndUnit = ((size_t)pMem + nCurrentUnitSize) / (nMaxMemoryPerUnit);
if (nStartUnit > nUnitsPerLine)
{
nStartUnit = nUnitsPerLine;
}
if (nEndUnit > nUnitsPerLine)
{
nEndUnit = nUnitsPerLine;
}
for (size_t i = nStartUnit; i < nEndUnit; ++i)
{
vCoverage[i] = 1;
}
}
++nLocalUnits;
// insert in linked list
*pMem = pMemoryBlocks;
pMemoryBlocks = pMem;
}
nUnits += nLocalUnits;
nAvailableMem += nLocalUnits * nCurrentUnitSize;
nCurrentUnitSize /= 2;
nUnits *= 2;
}
// free all memory blocks allocated
while (pMemoryBlocks)
{
void* pNext = *pMemoryBlocks;
::free(pMemoryBlocks);
++dwFreeCnt;
pMemoryBlocks = (void**)pNext;
}
// _heapmin();
CryLog("CMemoryFragmentationProfiler Y=%d, available memory=%d MB, used memory=%d MB",
m_dwLine, (nAvailableMem + 1024 * 1024 - 1) / (1024 * 1024), (nUsedMem + 1024 * 1024 - 1) / (1024 * 1024));
LogCoverage(vCoverage);
DumpToRAWCoverage(vCoverage);
}
private: // ------------------------------------------------------------------
void Init()
{
FILE* out = nullptr;
azfopen(&out, "MemoryCoverage.bmp", "wb");
if (out)
{
BITMAPFILEHEADER pHeader;
BITMAPINFOHEADER pInfoHeader;
memset(&pHeader, 0, sizeof(BITMAPFILEHEADER));
memset(&pInfoHeader, 0, sizeof(BITMAPINFOHEADER));
pHeader.bfType = 0x4D42;
pHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + m_nPixelsPerLine * m_nLineCount * 3;
pHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
pInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
pInfoHeader.biWidth = m_nPixelsPerLine;
pInfoHeader.biHeight = m_nLineCount;
pInfoHeader.biPlanes = 1;
pInfoHeader.biBitCount = 24;
pInfoHeader.biCompression = 0;
pInfoHeader.biSizeImage = m_nPixelsPerLine * m_nLineCount;
fwrite(&pHeader, 1, sizeof(BITMAPFILEHEADER), out);
fwrite(&pInfoHeader, 1, sizeof(BITMAPINFOHEADER), out);
for (int y = 0; y < m_nLineCount; y++) // amount of lines
{
for (int x = 0; x < m_nPixelsPerLine; x++)
{
size_t nAddress = x * (0x100000000 / m_nPixelsPerLine);
if (nAddress == 0x30000000
|| nAddress == 0x30500000
|| nAddress == 0x31000000
|| nAddress == 0x31500000
|| nAddress == 0x32000000
|| nAddress == 0x32500000
|| nAddress == 0x33500000
|| nAddress == 0x34000000
|| nAddress == 0x35000000
|| nAddress == 0x35500000
|| nAddress == 0x36000000
|| nAddress == 0x36500000
|| nAddress == 0x38000000
|| nAddress == 0x39000000)
{
putc((unsigned char)100, out); // blue DLL start
}
else
{
putc((unsigned char)0, out); // black
}
putc((unsigned char)0, out);
putc((unsigned char)0, out);
}
}
fclose(out);
m_dwLine = 0;
}
}
void LogCoverage(std::vector<bool>& vCov)
{
const size_t nCharPerLine = 128; // readable amount
char szResult[nCharPerLine + 1], * pCursor = szResult;
szResult[nCharPerLine] = 0; // zero termination
size_t nSize = vCov.size();
size_t nUnitsPerChar = nSize / nCharPerLine;
for (size_t i = 0; i < nSize; )
{
unsigned int nLocalCov = 0;
for (size_t e = 0; e < nUnitsPerChar; ++e, ++i)
{
if (vCov[i])
{
++nLocalCov;
}
}
if (nLocalCov == 0)
{
*pCursor++ = '#'; // occupied
}
else if (nLocalCov == nUnitsPerChar)
{
*pCursor++ = '.'; // free
}
else
{
*pCursor++ = '+'; // partly
}
}
CryLog(" Coverage=%s", szResult);
}
void DumpToRAWCoverage(std::vector<bool>& vCov)
{
FILE* out = nullptr;
azfopen(&out, "MemoryCoverage.bmp", "rb+");
if (!out)
{
return;
}
if (fseek(out, sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 3 * (m_nLineCount - 1 - m_dwLine) * m_nPixelsPerLine, SEEK_SET) != 0)
{
fclose(out);
return;
}
size_t nSize = vCov.size();
size_t nUnitsPerChar = nSize / m_nPixelsPerLine;
for (size_t i = 0; i < nSize; )
{
unsigned int nLocalCov = 0;
for (size_t e = 0; e < nUnitsPerChar; ++e, ++i)
{
if (vCov[i])
{
++nLocalCov;
}
}
size_t Val = 256 - (256 * nLocalCov) / nUnitsPerChar;
if (Val > 0)
{
Val = 127 + Val / 2;
}
putc((unsigned char)Val, out);
putc((unsigned char)Val, out);
putc((unsigned char)Val, out); // grey
}
fclose(out);
++m_dwLine;
}
unsigned int m_dwLine; // [0..m_nLineCount-1], m_nLineCount means bitmap is full, 0xffffffff means not initialized yet
static const size_t m_nPixelsPerLine = 1024; // bitmap width
static const size_t m_nLineCount = 128; //
};
#else // defined(WIN32) || defined(WIN64)
class CMemoryFragmentationProfiler
{
public:
void DumpMemoryCoverage() {}
};
#endif // defined(WIN32) || defined(WIN64)
#endif // CRYINCLUDE_CRYSYSTEM_MEMORYFRAGMENTATIONPROFILER_H