跟踪堆分配请求
更新:2007 年 11 月
本主题适用于:
版本 |
Visual Basic |
C# |
C++ |
Web Developer |
---|---|---|---|---|
速成版 |
仅限本机 |
|||
标准版 |
仅限本机 |
|||
专业团队版 |
仅限本机 |
表格图例:
适用 |
|
不适用 |
|
默认情况下隐藏的一条或多条命令。 |
尽管查明在其中执行断言或报告宏的源文件名和行号对于定位问题原因常常很有用,对于堆分配函数却可能不是这样。虽然可在应用程序的逻辑树中的许多适当点插入宏,但分配经常隐藏在特殊例程中,该例程会在很多不同时刻从很多不同位置进行调用。问题通常并不在于如何确定哪行代码进行了错误分配,而在于如何确定该行代码进行的上千次分配中的哪一次是错误分配以及原因。
唯一分配请求编号和 _crtBreakAlloc
标识发生错误的特定堆分配调用的最简单方法是利用与调试堆中的每个块关联的唯一分配请求编号。当其中一个转储函数报告某块的有关信息时,该分配请求编号将括在大括号中(例如“{36}”)。
知道某个错误分配块的分配请求编号后,可以将该编号传递给 _CrtSetBreakAlloc 以创建一个断点。执行将恰在分配该块以前中断,您可以向回追踪以确定哪个例程执行了错误调用。为避免重新编译,可以在调试器中完成同样的操作,方法是将 _crtBreakAlloc 设置为所感兴趣的分配请求编号。
创建分配例程的“Debug”版本
略微复杂的方法是创建您自己的分配例程的“Debug”版本,等同于堆分配函数的 _dbg 版本。然后,可以将源文件和行号参数传递给基础堆分配例程,并能立即看到错误分配的出处。
例如,假定您的应用程序包含与下面类似的常用例程:
int addNewRecord(struct RecStruct * prevRecord,
int recType, int recAccess)
{
// ...code omitted through actual allocation...
if ((newRec = malloc(recSize)) == NULL)
// ... rest of routine omitted too ...
}
在头文件中,可以添加如下代码:
#ifdef _DEBUG
#define addNewRecord(p, t, a) \
addNewRecord(p, t, a, __FILE__, __LINE__)
#endif
接下来,可以如下更改记录创建例程中的分配:
int addNewRecord(struct RecStruct *prevRecord,
int recType, int recAccess
#ifdef _DEBUG
, const char *srcFile, int srcLine
#endif
)
{
/* ... code omitted through actual allocation ... */
if ((newRec = _malloc_dbg(recSize, _NORMAL_BLOCK,
srcFile, scrLine)) == NULL)
/* ... rest of routine omitted too ... */
}
在其中调用 addNewRecord 的源文件名和行号将存储在产生的每个块中(这些块是在调试堆中分配的),并将在检查该块时进行报告。