err,hr
调试代码时,监视窗口里面填入“$err,hr”(@err,hr或err,hr)
它的意义相当于让调试器帮你获取GetLastError的值
Pseudovariables
- $err 显示通过SetLastError设置的错误代码,何在代码中调用GetLastError效果一样。可以用$err,hr来显示错误代码对应的文本信息
- $handles 显示应用程序中的句柄数量
- $vframe 显示当前栈帧的地址
- $tid 显示当前的线程ID
- $env 显示当前应用的环境变量
- $cmdline 显示运行当前应用的命令行参数
- $pid 显示进程ID
- $registername或@registername 显示寄存器的内容,如果寄存器的名字不和当前其他变量重名,直接写寄存器的名字也行
- $clk 显示clock cycles
- $user 显示运行当前用户的用户信息
在C#的程序中可以使用的Pseudovariables
- $exception 显示最后的异常信息
- $user 显示运行当前用户的用户信息
windbg
使用windbg分析dmp文件定位程序bug
https://debugging.wellisolutions.de/windbg-versions/
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-download-tools
Dbgview
DebugView跟踪调试信息
dump
生成dump文件
http://www.debuginfo.com/articles/effminidumps.html
http://www.cppblog.com/woaidongmao/archive/2011/05/10/146086.html
http://www.cppblog.com/woaidongmao/archive/2009/10/22/99210.html
http://www.cppblog.com/woaidongmao/archive/2009/10/21/99135.html
SetUnhandledExceptionFilter
如果在异常发生之前调用了SetUnhandledExceptionFilter()函数。
_set_purecall_handler
调用纯虚函数是一个错误
set_terminate
当CRT遇到一个未被处理的C++类型化异常时,它会调用terminate()函数
_set_purecall_handler
使用_set_purecall_handler()函数来处理纯虚函数调用
_set_new_handler
使用_set_new_handler()函数处理内存分配失败。
#include <iostream>
#include <new>
void handler()
{
std::cout << "Memory allocation failed, terminating\n";
std::set_new_handler(nullptr);
}
int main()
{
std::set_new_handler(handler);
try {
while (true) {
new int[100000000ul];
}
} catch (const std::bad_alloc& e) {
std::cout << e.what() << '\n';
}
}
_set_invalid_parameter_handler
当系统函数调用检测到非法的参数时,会使用_set_invalid_parameter_handler()函数来处理这种情况
XCrashReport
https://www.codeproject.com/Articles/5260/XCrashReport-Exception-Handling-and-Crash-Report
https://blog.csdn.net/ljmscsq/article/details/4998738
https://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus-可成功生成dump
https://www.cnblogs.com/hdtianfu/archive/2011/12/27/2303113.html
https://www.cnblogs.com/kanego/p/4081287.html
crashrpt
可以使用crashrpt
直接集成到自己的项目中,程序崩溃是生成错误报告
http://crashrpt.sourceforge.net/
http://crashrpt.sourceforge.net/docs/html/simple_example.html
crashrpt是一个包含能够在程序出现各种类型未处理异常时生成程序错误报告,然后将该报告按照指定的方式(例如HTTP或者SMTP)发送给开发者,最后分析这些信息的工具。crashrpt由3个部分组成,错误报告生成库CrashRpt,我们需要在自己的程序中使用该库捕获我们的程序没有处理的异常,在该库捕获到这些未处理的异常后,CrashRpt会生成MiniDump文件,并将和你使用该库指定的信息(例如日志文件和屏幕截图等)一起打包成错误报告。CrashRpt库支持处理我所知道的所有Windows C/C++程序抛出的各类异常,例如我前面提到过的SEH,它还能捕获C++异常、信号和调用各类CRT库中的函数出现的错误。异常信息发送工具CrashSender,该工具能够按照我们使用CrashRpt设置的方式,将生成的错误报告按照我们指定的方式(HTTP、SMTP或者MAPI)发送给我们。自动异常信息处理工具crprober,该工具能够在后台接收CrashSender发送给我们的错误报告,通过分析错误报告后以文本的形式输出程序的异常信息。
https://blog.csdn.net/shining100/article/details/7760872
http://crashrpt.sourceforge.net/docs/html/configuring_project.html
crashfix
crashfix CrashRpt的免费和开源服务器,接收、存储、组织及分析事故报告发送的 C + + 应用程序。
崩溃报告是一种允许接收用户反馈并提高软件质量和稳定性的技术。当您的应用遇到严重错误(也称为异常或崩溃)时,会生成崩溃报告文件并通过Internet发送到您的服务器。CrashFix服务器允许轻松接收,存储,组织和分析C ++应用程序发送的崩溃报告。
http://crashfix.sourceforge.net/
http://crashfix.sourceforge.net/doc/html/index.html
http://www.debuginfo.com
MINIDUMPS
http://www.debuginfo.com/articles/effminidumps.html
http://www.debuginfo.com/examples/effmdmpexamples.html
dbghelp
http://www.debuginfo.com/examples/dbghelpexamples.html
DEBUGGING API EXAMPLES
http://www.debuginfo.com/examples/dbgexamples.html
MINIDUMP WIZARD
http://www.debuginfo.com/tools/minidumpwizard.html
UNEXPECTED USER BREAKPOINT IN NTDLL.DLL
用户在断点意外 NTDLL. DLL
http://www.debuginfo.com/tips/userbpntdll.html
https://www.cnblogs.com/lidabo/p/3706725.html
About DbgHelp
STL扩展库
关于MFC中的#ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #endif
情况1:
#ifdef _DEBUG
virtual void AssertValid() const; //assert(断言)valid(有效的,正确的)
virtual void Dump(CDumpContext& dc) const; //存储上下文
#endif
这两个函数是调试用的,第一个函数检查可用性,即是否有效。第二个函数如果未更改的话,最终调用的是Cwnd::Dump();输出窗口类名,标题名等一系列信息(在输出窗口中)。
#ifdef _DEBUG
#endif
这是条件编译,即如果有#define _DEBUG这两个函数会编译,否则忽略。当你用debug生成时(相对于release)开发环境则自动的加上这个宏定义,这两个函数有效。
情况2:
#ifdef _DEBUG // 判断是否定义_DEBUG
#undef THIS_FILE // 取消THIS_FILE的定义
static char THIS_FILE[]=__FILE__; // 定义THIS_FILE指向文件名
#define new DEBUG_NEW // 定义调试new宏,取代new关键字
#endif // 结束
如果定义了_DEBUG,表示在调试状态下编译,因此相应修改了两个符号的定义。THIS_FILE
是一个char数组全局变量,字符串值为当前文件的全路径,这样在Debug版本中当程序出错时出错处理代码可用这个变量告诉你是哪个文件中的代码有问题。
定义 _DEBUG后,由于定义了_DEBUG,编译器确定这是一个调试,编译#ifdef _DEBUG
和#endif
之间的代码。#undef
表示清除当前定义的宏,使得THIS_FILE
无定义。__FILE__
是编译器能识别的事先定义的ANSI C 的6个宏之一。
DEBUG_NEW定位内存泄露并且跟踪文件名和行号。
情况3:
#ifdef _DEBUG //如果是debug状态
#undef THIS_FILE //清除THIS_FILE
static char THIS_FILE[]=__FILE__; //定义THIS_FILE为__FILE__(这是当前文件全路径名字)
#define new DEBUG_NEW //定义new为DEBUG_NEW(这个可以检测到内存泄露之类的问题,其实就是可以使用crt开头的那几个调试函数)
#endif
ANSI C 的6个宏:
__FILE__为预编译器常量,返回当前编译的文件名,还有比较常用的几个预编译器常量;
__LINE__编译器正在编译的文件的第几行;
__DATE__返回当前的日期Jul-20-2004;
__TIME__返回当前的时间hh:mm:ss;
__TIMESTAMP__ 的预定义的编译器宏始终返回时间戳信息。在太平洋标准的时间内无论本地时间和CL.EXE 的运行位置在计算机上的时区。
__STDC__条件编译,意思是:如果定义了标准C或c++,那么编译这句话后面直到#endif以前的源代码。
_STDC__cplusplus这两个都是标准宏,_STDC_表示是是否符合标准C;_cplusplus表示是否是C++。
曾经一个解释说,多次使用__FILE__
宏,虽然得到字符串的内容相同,但是可能地址不同,即同一个字符串常量多次用到时占用不同的地址,这样导致需要的内存增加了。为了检测内在泄露, Debug 版本的 new 附加上了调用 new 的文件名与调用所在的行号信息, 这是通过 __FILE__
和 __LINE__
来实现的, 这两个属于预定义的内部宏, 而之所以要用 THIS_FILE
来代替 __FILE__
, 是为了减少程序大小: 如果你在一个文件中有 10000 次对 new 的调用, 那么会生成 10000 个当前文件名的常量字符串(第一个都是由 __FILE__
宏扩展而来的), 最后生成的目标文件会很大, 而用 THIS_FILE
来代替, 当前文件名只有一份, 传递文件名使用 THIS_FILE
指针就可以了。__FILE__
和__LINE__
一样都是编译器定义的宏。当碰到__FILE__
时,编译器会把__FILE__
替换成一个字符串,这个字符串就是当前在编译的文件的路径名。在DEBUG_NEW
的定义中没有直接使用__FILE__
,而是用了THIS_FILE
,其目的是为了减小目标文件的大小。假设在某个cpp文件中有100处使用了new,如果直接使用__FILE__
,那编译器会产生100个常量字符串,这100个字符串都是这个cpp文件的路径名,显然十分冗余。如果使用THIS_FILE
,编译器只会产生一个常量字符串,那100处new的调用使用的都是指向常量字符串的指针。
在MFC中,可以使用 DEBUG_NEW
宏代替 new 运算符来帮助定位内存泄漏。在程序的“Debug”版本中,DEBUG_NEW
将为所分配的每个对象跟踪文件名和行号。当编译程序的“Release”版本时,DEBUG_NEW
将解析为不包含文件名和行号信息的简单 new 操作。因此,在程序的“Release”版本中不会造成任何速度损失。
最快速度找到内存泄漏
dmp调试
使用转储文件在 Visual Studio 中调试应用程序崩溃和挂起
exe + pdb + dmp 在同一目录,然后双击dmp文件VC就可以打开,然后点击右上角的调试按钮就可以直接定位到崩溃的代码了;
适用于Windows的调试工具(WinDbg,KD,CDB,NTSD)
SEH(结构化异常处理)
CRT
MemTrack
Appendix A: MemTrack.h
/*
Copyright (c) 2002, 2008 Curtis Bartley
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
- Neither the name of Curtis Bartley nor the names of any other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef MemTrack_H_
#define MemTrack_H_
#include <typeinfo>
namespace MemTrack
{
/* ---------------------------------------- class MemStamp */
class MemStamp
{
public: // member variables
char const * const filename;
int const lineNum;
public: // construction/destruction
MemStamp(char const *filename, int lineNum)
: filename(filename), lineNum(lineNum) { }
~MemStamp() { }
};
/* ---------------------------------------- memory allocation and stamping prototypes */
void *TrackMalloc(size_t size);
void TrackFree(void *p);
void TrackStamp(void *p, const MemStamp &stamp, char const *typeName);
void TrackDumpBlocks();
void TrackListMemoryUsage();
/* ---------------------------------------- operator * (MemStamp, ptr) */
template <class T> inline T *operator*(const MemStamp &stamp, T *p)
{
TrackStamp(p, stamp, typeid(T).name());
return p;
}
} // namespace MemTrack
/* ---------------------------------------- new macro */
#define MEMTRACK_NEW MemTrack::MemStamp(__FILE__, __LINE__) * new
#define new MEMTRACK_NEW
#endif // MemTrack_H_
Appendix B: MemTrack.cpp
/*
Copyright (c) 2002, 2008 Curtis Bartley
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
- Neither the name of Curtis Bartley nor the names of any other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* ---------------------------------------- includes */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <new>
#include "MemTrack.h"
#undef new // IMPORTANT!
/* ------------------------------------------------------------ */
/* -------------------- namespace MemTrack -------------------- */
/* ------------------------------------------------------------ */
namespace MemTrack
{
/* ------------------------------------------------------------ */
/* --------------------- class BlockHeader -------------------- */
/* ------------------------------------------------------------ */
class BlockHeader
{
private: // static member variables
static BlockHeader *ourFirstNode;
private: // member variables
BlockHeader *myPrevNode;
BlockHeader *myNextNode;
size_t myRequestedSize;
char const *myFilename;
int myLineNum;
char const *myTypeName;
public: // members
BlockHeader(size_t requestedSize);
~BlockHeader();
size_t GetRequestedSize() const { return myRequestedSize; }
char const *GetFilename() const { return myFilename; }
int GetLineNum() const { return myLineNum; }
char const *GetTypeName() const { return myTypeName; }
void Stamp(char const *filename, int lineNum, char const *typeName);
static void AddNode(BlockHeader *node);
static void RemoveNode(BlockHeader *node);
static size_t CountBlocks();
static void GetBlocks(BlockHeader **blockHeaderPP);
static bool TypeGreaterThan(BlockHeader *header1, BlockHeader *header2);
};
/* ---------------------------------------- BlockHeader static member variables */
BlockHeader *BlockHeader::ourFirstNode = NULL;
/* ---------------------------------------- BlockHeader constructor */
BlockHeader::BlockHeader(size_t requestedSize)
{
myPrevNode = NULL;
myNextNode = NULL;
myRequestedSize = requestedSize;
myFilename = "[unknown]";
myLineNum = 0;
myTypeName = "[unknown]";
}
/* ---------------------------------------- BlockHeader destructor */
BlockHeader::~BlockHeader()
{
}
/* ---------------------------------------- BlockHeader Stamp */
void BlockHeader::Stamp(char const *filename, int lineNum, char const *typeName)
{
myFilename = filename;
myLineNum = lineNum;
myTypeName = typeName;
}
/* ---------------------------------------- BlockHeader AddNode */
void BlockHeader::AddNode(BlockHeader *node)
{
assert(node != NULL);
assert(node->myPrevNode == NULL);
assert(node->myNextNode == NULL);
// If we have at least one node in the list ...
if (ourFirstNode != NULL)
{
// ... make the new node the first node's predecessor.
assert(ourFirstNode->myPrevNode == NULL);
ourFirstNode->myPrevNode = node;
}
// Make the first node the new node's succesor.
node->myNextNode = ourFirstNode;
// Make the new node the first node.
ourFirstNode = node;
}
/* ---------------------------------------- BlockHeader RemoveNode */
void BlockHeader::RemoveNode(BlockHeader *node)
{
assert(node != NULL);
assert(ourFirstNode != NULL);
// If target node is the first node in the list...
if (ourFirstNode == node)
{
// ... make the target node's successor the first node.
assert(ourFirstNode->myPrevNode == NULL);
ourFirstNode = node->myNextNode;
}
// Link target node's predecessor, if any, to its successor.
if (node->myPrevNode != NULL)
{
node->myPrevNode->myNextNode = node->myNextNode;
}
// Link target node's successor, if any, to its predecessor.
if (node->myNextNode != NULL)
{
node->myNextNode->myPrevNode = node->myPrevNode;
}
// Clear target node's previous and next pointers.
node->myPrevNode = NULL;
node->myNextNode = NULL;
}
/* ---------------------------------------- BlockHeader CountBlocks */
size_t BlockHeader::CountBlocks()
{
size_t count = 0;
BlockHeader *currNode = ourFirstNode;
while (currNode != NULL)
{
count++;
currNode = currNode->myNextNode;
}
return count;
}
/* ---------------------------------------- BlockHeader GetBlocks */
void BlockHeader::GetBlocks(BlockHeader **blockHeaderPP)
{
BlockHeader *currNode = ourFirstNode;
while (currNode != NULL)
{
*blockHeaderPP = currNode;
blockHeaderPP++;
currNode = currNode->myNextNode;
}
}
/* ---------------------------------------- BlockHeader TypeGreaterThan */
bool BlockHeader::TypeGreaterThan(BlockHeader *header1, BlockHeader *header2)
{
return (strcmp(header1->myTypeName, header2->myTypeName) > 0);
}
/* ------------------------------------------------------------ */
/* ---------------------- class Signature --------------------- */
/* ------------------------------------------------------------ */
class Signature
{
private: // constants
static const unsigned int SIGNATURE1 = 0xCAFEBABE;
static const unsigned int SIGNATURE2 = 0xFACEFACE;
private: // member variables
unsigned int mySignature1;
unsigned int mySignature2;
public: // construction/destruction
Signature() : mySignature1(SIGNATURE1), mySignature2(SIGNATURE2) {};
~Signature() { mySignature1 = 0; mySignature2 = 0; }
public: // static member functions
static bool IsValidSignature(const Signature *pProspectiveSignature)
{
try
{
if (pProspectiveSignature->mySignature1 != SIGNATURE1) return false;
if (pProspectiveSignature->mySignature2 != SIGNATURE2) return false;
return true;
}
catch (...)
{
return false;
}
}
};
/* ------------------------------------------------------------ */
/* -------------------- address conversion -------------------- */
/* ------------------------------------------------------------ */
/* We divide the memory blocks we allocate into two "chunks", the
* "prolog chunk" where we store information about the allocation,
* and the "user chunk" which we return to the caller to use.
*/
/* ---------------------------------------- alignment */
const size_t ALIGNMENT = 4;
/* If "value" (a memory size or offset) falls on an alignment boundary,
* then just return it. Otherwise return the smallest number larger
* than "value" that falls on an alignment boundary.
*/
#define PAD_TO_ALIGNMENT_BOUNDARY(value) \
((value) + ((ALIGNMENT - ((value) % ALIGNMENT)) % ALIGNMENT))
/* ---------------------------------------- chunk structs */
/* We declare incomplete structures for each chunk, just to
* provide type safety.
*/
struct PrologChunk;
struct UserChunk;
/* ---------------------------------------- chunk sizes and offsets */
const size_t SIZE_BlockHeader = PAD_TO_ALIGNMENT_BOUNDARY(sizeof(BlockHeader));
const size_t SIZE_Signature = PAD_TO_ALIGNMENT_BOUNDARY(sizeof(Signature));
const size_t OFFSET_BlockHeader = 0;
const size_t OFFSET_Signature = OFFSET_BlockHeader + SIZE_BlockHeader;
const size_t OFFSET_UserChunk = OFFSET_Signature + SIZE_Signature;
const size_t SIZE_PrologChunk = OFFSET_UserChunk;
/* ---------------------------------------- GetUserAddress */
static UserChunk *GetUserAddress(PrologChunk *pProlog)
{
char *pchProlog = reinterpret_cast<char *>(pProlog);
char *pchUser = pchProlog + OFFSET_UserChunk;
UserChunk *pUser = reinterpret_cast<UserChunk *>(pchUser);
return pUser;
}
/* ---------------------------------------- GetPrologAddress */
static PrologChunk *GetPrologAddress(UserChunk *pUser)
{
char *pchUser = reinterpret_cast<char *>(pUser);
char *pchProlog = pchUser - OFFSET_UserChunk;
PrologChunk *pProlog = reinterpret_cast<PrologChunk *>(pchProlog);
return pProlog;
}
/* ---------------------------------------- GetHeaderAddress */
static BlockHeader *GetHeaderAddress(PrologChunk *pProlog)
{
char *pchProlog = reinterpret_cast<char *>(pProlog);
char *pchHeader = pchProlog + OFFSET_BlockHeader;
BlockHeader *pHeader = reinterpret_cast<BlockHeader *>(pchHeader);
return pHeader;
}
/* ---------------------------------------- GetSignatureAddress */
static Signature *GetSignatureAddress(PrologChunk *pProlog)
{
char *pchProlog = reinterpret_cast<char *>(pProlog);
char *pchSignature = pchProlog + OFFSET_Signature;
Signature *pSignature = reinterpret_cast<Signature *>(pchSignature);
return pSignature;
}
/* ------------------------------------------------------------ */
/* -------------- memory allocation and stamping -------------- */
/* ------------------------------------------------------------ */
/* ---------------------------------------- TrackMalloc */
void *TrackMalloc(size_t size)
{
// Allocate the memory, including space for the prolog.
PrologChunk *pProlog = (PrologChunk *)malloc(SIZE_PrologChunk + size);
// If the allocation failed, then return NULL.
if (pProlog == NULL) return NULL;
// Use placement new to construct the block header in place.
BlockHeader *pBlockHeader = new (pProlog) BlockHeader(size);
// Link the block header into the list of extant block headers.
BlockHeader::AddNode(pBlockHeader);
// Use placement new to construct the signature in place.
Signature *pSignature = new (GetSignatureAddress(pProlog)) Signature;
// Get the offset to the user chunk and return it.
UserChunk *pUser = GetUserAddress(pProlog);
return pUser;
}
/* ---------------------------------------- TrackFree */
void TrackFree(void *p)
{
// It's perfectly valid for "p" to be null; return if it is.
if (p == NULL) return;
// Get the prolog address for this memory block.
UserChunk *pUser = reinterpret_cast<UserChunk *>(p);
PrologChunk *pProlog = GetPrologAddress(pUser);
// Check the signature, and if it's invalid, return immediately.
Signature *pSignature = GetSignatureAddress(pProlog);
if (!Signature::IsValidSignature(pSignature)) return;
// Destroy the signature.
pSignature->~Signature();
pSignature = NULL;
// Unlink the block header from the list and destroy it.
BlockHeader *pBlockHeader = GetHeaderAddress(pProlog);
BlockHeader::RemoveNode(pBlockHeader);
pBlockHeader->~BlockHeader();
pBlockHeader = NULL;
// Free the memory block.
free(pProlog);
}
/* ---------------------------------------- TrackStamp */
void TrackStamp(void *p, const MemStamp &stamp, char const *typeName)
{
// Get the header and signature address for this pointer.
UserChunk *pUser = reinterpret_cast<UserChunk *>(p);
PrologChunk *pProlog = GetPrologAddress(pUser);
BlockHeader *pHeader = GetHeaderAddress(pProlog);
Signature *pSignature = GetSignatureAddress(pProlog);
// If the signature is not valid, then return immediately.
if (!Signature::IsValidSignature(pSignature)) return;
// "Stamp" the information onto the header.
pHeader->Stamp(stamp.filename, stamp.lineNum, typeName);
}
/* ---------------------------------------- TrackDumpBlocks */
void TrackDumpBlocks()
{
// Get an array of pointers to all extant blocks.
size_t numBlocks = BlockHeader::CountBlocks();
BlockHeader **ppBlockHeader =
(BlockHeader **)calloc(numBlocks, sizeof(*ppBlockHeader));
BlockHeader::GetBlocks(ppBlockHeader);
// Dump information about the memory blocks.
printf("\n");
printf("=====================\n");
printf("Current Memory Blocks\n");
printf("=====================\n");
printf("\n");
for (size_t i = 0; i < numBlocks; i++)
{
BlockHeader *pBlockHeader = ppBlockHeader[i];
char const *typeName = pBlockHeader->GetTypeName();
size_t size = pBlockHeader->GetRequestedSize();
char const *fileName = pBlockHeader->GetFilename();
int lineNum = pBlockHeader->GetLineNum();
printf("*** #%-6d %5d bytes %-50s\n", i, size, typeName);
printf("... %s:%d\n", fileName, lineNum);
}
// Clean up.
free(ppBlockHeader);
}
/* ---------------------------------------- struct MemDigest */
struct MemDigest
{
char const *typeName;
int blockCount;
size_t totalSize;
static bool TotalSizeGreaterThan(const MemDigest &md1, const MemDigest &md2)
{ return md1.totalSize > md2.totalSize; }
};
/* ---------------------------------------- SummarizeMemoryUsageForType */
static void SummarizeMemoryUsageForType(
MemDigest *pMemDigest,
BlockHeader **ppBlockHeader,
size_t startPost,
size_t endPost
)
{
pMemDigest->typeName = ppBlockHeader[startPost]->GetTypeName();
pMemDigest->blockCount = 0;
pMemDigest->totalSize = 0;
for (size_t i = startPost; i < endPost; i++)
{
pMemDigest->blockCount++;
pMemDigest->totalSize += ppBlockHeader[i]->GetRequestedSize();
assert(strcmp(ppBlockHeader[i]->GetTypeName(), pMemDigest->typeName) == 0);
}
}
/* ---------------------------------------- TrackListMemoryUsage */
void TrackListMemoryUsage()
{
// If there are no allocated blocks, then return now.
size_t numBlocks = BlockHeader::CountBlocks();
if (numBlocks == 0) return;
// Get an array of pointers to all extant blocks.
BlockHeader **ppBlockHeader =
(BlockHeader **)calloc(numBlocks, sizeof(*ppBlockHeader));
BlockHeader::GetBlocks(ppBlockHeader);
// Sort the blocks by type name.
std::sort(
ppBlockHeader,
ppBlockHeader + numBlocks,
BlockHeader::TypeGreaterThan
);
// Find out how many unique types we have.
size_t numUniqueTypes = 1;
for (size_t i = 1; i < numBlocks; i++)
{
char const *prevTypeName = ppBlockHeader[i - 1]->GetTypeName();
char const *currTypeName = ppBlockHeader[i]->GetTypeName();
if (strcmp(prevTypeName, currTypeName) != 0) numUniqueTypes++;
}
// Create an array of "digests" summarizing memory usage by type.
size_t startPost = 0;
size_t uniqueTypeIndex = 0;
MemDigest *pMemDigestArray =
(MemDigest *)calloc(numUniqueTypes, sizeof(*pMemDigestArray));
for (size_t i = 1; i <= numBlocks; i++) // yes, less than or *equal* to
{
char const *prevTypeName = ppBlockHeader[i - 1]->GetTypeName();
char const *currTypeName = (i < numBlocks) ? ppBlockHeader[i]->GetTypeName() : "";
if (strcmp(prevTypeName, currTypeName) != 0)
{
size_t endPost = i;
SummarizeMemoryUsageForType(
pMemDigestArray + uniqueTypeIndex,
ppBlockHeader,
startPost,
endPost
);
startPost = endPost;
uniqueTypeIndex++;
}
}
assert(uniqueTypeIndex = numUniqueTypes);
// Sort the digests by total memory usage.
std::sort(
pMemDigestArray,
pMemDigestArray + numUniqueTypes,
MemDigest::TotalSizeGreaterThan
);
// Compute the grand total memory usage.
size_t grandTotalNumBlocks = 0;
size_t grandTotalSize = 0;
for (size_t i = 0; i < numUniqueTypes; i++)
{
grandTotalNumBlocks += pMemDigestArray[i].blockCount;
grandTotalSize += pMemDigestArray[i].totalSize;
}
// Dump the memory usage statistics.
printf("\n");
printf("-----------------------\n");
printf("Memory Usage Statistics\n");
printf("-----------------------\n");
printf("\n");
printf("%-50s%5s %5s %7s %s \n", "allocated type", "blocks", "", "bytes", "");
printf("%-50s%5s %5s %7s %s \n", "--------------", "------", "", "-----", "");
for (size_t i = 0; i < numUniqueTypes; i++)
{
MemDigest *pMD = pMemDigestArray + i;
size_t blockCount = pMD->blockCount;
double blockCountPct = 100.0 * blockCount / grandTotalNumBlocks;
size_t totalSize = pMD->totalSize;
double totalSizePct = 100.0 * totalSize / grandTotalSize;
printf(
"%-50s %5d %5.1f%% %7d %5.1f%%\n",
pMD->typeName,
blockCount,
blockCountPct,
totalSize,
totalSizePct
);
}
printf("%-50s %5s %5s %7s %s \n", "--------", "-----", "", "-------", "");
printf("%-50s %5d %5s %7d %s \n", "[totals]", grandTotalNumBlocks, "", grandTotalSize, "");
// Clean up.
free(ppBlockHeader);
free(pMemDigestArray);
}
} // namespace MemTrack
/* ------------------------------------------------------------ */
/* ---------------------- new and delete ---------------------- */
/* ------------------------------------------------------------ */
/* ---------------------------------------- operator new */
void *operator new(size_t size)
{
void *p = MemTrack::TrackMalloc(size);
if (p == NULL) throw std::bad_alloc();
return p;
}
/* ---------------------------------------- operator delete */
void operator delete(void *p)
{
MemTrack::TrackFree(p);
}
/* ---------------------------------------- operator new[] */
void *operator new[](size_t size)
{
void *p = MemTrack::TrackMalloc(size);
if (p == NULL) throw std::bad_alloc();
return p;
}
/* ---------------------------------------- operator delete[] */
void operator delete[](void *p)
{
MemTrack::TrackFree(p);
}