VC调试技巧
Visual C++ 的 C 运行时刻函数库标识模板
0xCD 已经分配的数据(alloCated Data)
0xDD 已经释放的数据(Deleted Data)
0xFD 被保护的数据(Fence Data)
Visual C++ 的 C 运行时刻函数库内存块类型标识符
_NORMAL_BLOCK 由程序直接分配的内存
_CLIENT_BLOCK 由程序直接分配的内存,可以通过内存调试函数对其拥有特殊控制权
_CRT_BLOCK 由运行时刻函数库内部分配的内存
_FREE_BLOCK 已经被释放,但是跟踪仍然被保留下来的内存,这在用户选择了调试堆的选项 _CRTDBG_DELAY_FREE_MEM_DF 以后会出现
_IGNORE_BLOCK 当使用 _CrtDbgFlag 关闭内存调试操作以后分配的内存
Visual C++ 的 C 运行时刻函数库提供的帮助调试内存错误的函数
_CrtCheckMemory 检查每一个内存块的内部数据结构和守护(guard)字节,以测试其完整性。
_CrtIsValidHeapPointer 检验指定指针是否存在于本地堆中
_CrtIsValidPointer 检验给定内存范围对读写操作是否合法
_CrtIsMemoryBlock 检验给定内存范围是否位于本地堆当中,是否拥有例如 _NORMAL_BLOCK 这样的有效内存块类型标识符(该函数还可以被用以获得分配数目以及进行内存分配的源文件名和行号)
用于调试内存泄露的 Visual C++ 的 C 运行时刻函数库中的函数
_CrtSetBreakAlloc 在给定的分配数目上分配断点,每一块被分配的内存都被指派一个连续的分配号。(查找特定的内存泄露十分有用)
_CrtDumpMemoryLeaks 判断内存泄露是否发生。如果发生则将本地堆中所有当前分配的内存按照用户可以阅读的方式进行内存映象转储。(在程序结束的时候检测内存泄露十分有用)
_CrtMemCheckPoint 在 _CrtMemState 结构中产生一个本地堆的当前状态的快照
_CrtMemDifference 比较两个堆中的断点,将不同之处保存在 _CrtMemState 结构中。如果不同则返真。(检测特殊区域代码的内存泄露十分有用)
_CrtMemDumpAllObjectsSince将从给定堆断点或者从程序开始分配的内存的所有信息按照用户可以阅读的方式进行内存映象转储
_CrtMemDumpStatistics 将信息按照用户可以阅读的方式进行内存映象转储到一个 _CrtMemState 结构中。(对于得到被使用的动态内存的全面观察信息来说十分有用)
用于一般内存调试的 Visual C++ 的 C 运行时刻函数库中的函数
_CrtSetDbgFlag 控制内存调试函数的行为
_CrtSetAllocHook 加裁在内存分配过程中的钩子函数。(对于检测内存使用状况或者模拟内存不足情况十分有用)
_CrtSetReportHook 加裁进行定制报告处理的函数。
_CrtSetDumpClient 加裁对用户进行内存映象转储的函数。
_CrtDoForAllClientObject 对于所有作为用户块进行分配的数据,调用指定的函数
ATL 内存调试
在 #include <AtlCom.h> 之前,定义控制预处理的常量 _ATL_DEBUG_INTERFACES,这样就可以对接口资源泄露进行跟踪(跟踪 AddRef 和 Release)
INTERFACE LEAK: RefCount = 7, MaxRefCount = 10, {Allocation = 42}
CMyComClass - Leak
然后在服务器初始化的时候对 CComModule 对象的 m_nIndexBreakAt 成员变量进行设置
#define _ATL_DEBUG_INTERFACES
BOOL WINAPI DllMain(...)
{
if (dwReason == DLL_PROCESS_ATTACH) {
...
_Module.m_nIndexBreakAt = 42; // Set breakpoint to find interface leak
}
...
}
使用调试堆
必须使用程序的调试版本,连接的是 C 运行时刻函数库的调试版本,必须定义 _DEBUG,这样调试堆版本的 new 和 delete 才会被调用。
调试堆选项
_CRTDBG_ALLOC_MEME_DF,启动堆分配检查
_CRTDBG_DELAY_FREE_MEM_DF,阻止内存被真正释放
_CRTDBG_CHECK_ALWAYS_DF,每次内存分配和释放都调用 _CrtCheckMemory
_CRTDBG_CHECK_CRT_DF,一般不使用
_CRTDBG_LEAK_CHECK_DF,在程序结束时调用 _CrtDumpMemoryLeaks
推荐总是使用 _CRTDBG_ALLOC_MEM_DF 和 _CRTDBG_LEAK_CHECK_DF,仅仅在调试内存错误时才用 _CRTDBG_CHECK_ALWAYS_DF 和 _CRTDBG_DELAY_FREE_MEM_DF,
显示内存泄露
#define _CRTDBG_MAP_ALLOC 在所有头文件之前,
在 cpp 文件中,加上
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
查看 Windows 内存地址
Windows 进程一般放在 0x00400000 的地址,0x00400000 是所有版本的 Windows 能使用的最低地址,进程实例句柄的值总是和它的基地址相同,
所有未被初始化的自动变量都会设上 0xCCCCCCCC,
Windows 2000 的虚拟地址空间的使用
0x00030000 ~ 0x0012FFFF 线程栈
0x00130000 ~ 0x003FFFFF 堆(有时堆位于此处)
0x00400000 ~ 0x005FFFFF 可执行代码
0x00600000 ~ 0x0FFFFFFF 堆(有时堆位于此处)
0x10000000 ~ 0x5FFFFFFF App Dlls, Msvcrt.dll, Mfc42.dll
0x77000000 ~ 0xFFFFFFFF Advapi32.dll,...
通过设置数据断点,在对 0xCDCDCDCD,0xCCCCCCCC,0xDDDDDDDD 等地址修改时,调试器会提醒你,写非合法数据
调试比较难的内存破坏问题时,可以试试 _CRTDBG_CHECK_ALWAYS_DF 和 _CRTDBG_DELAY_FREE_MEM_DF,
当类需要析构函数或者复制构造函数或者赋值操作符时,它同时需要这三个,否则可能导致内存破坏和泄露,
用分配号定位内存泄露
_CrtSetBreakAlloc(27)
Watch 窗口 {,,msvcrtd.dll}_CrtSetBreakAlloc(27)
使用内存检查点
void LeakyFunction()
{
_CrtMemState oldState, newState, stateDiff;
_CrtMemCheckPoint(&oldState);
{
...
}
_CrtMemCheckPoint(&newState);
if (_CrtMemeDifference(&stateDiff, &oldState, &newState)) {
_CrtMemDumpStatistics(&stateDiff);
_CrtMemDumpAllObjectsSince(&oldState);
}
}
使用 _CRTDBG_DELAY_FREE_MEM_DF 调试堆选项防止 _CrtMemDumpAllObjectsSince 导出错误的结果
在删除图形设备接口对象前,一定确定它们没有被任何有效的设备上下文选中
在 Windows 2000 里发现资源泄露是最简单方法是运行性能监视工具,监视程序的私有空间和句柄数随时间的变化,如果私有空间或者句柄数据持续增长,就出现了内存泄露
函数的返回值是通过 EAX 传递的,
--------------------------------------------------------------------------------------------
函数运行时间
@CLK,d
@CLK,0
函数返回值
32位 - @EAX
64位 - @EAX(低32位),@EDX(高32位)
大于64位,会在EAX中放入指向返回值指针,如返回一个 CRect, (CRect *) @EAX / 在内存窗口的Address栏中键入EAX查看
API 调用失败,键入@ERR可查看 GetLastError()的值, 翻译错误代码"@ERR,hr"
Windows自身会创建退出代码为 -1 的线程,如显示一通用对话框时,不用担心其返回为 -1,
关闭 GDI 的批处理功能,GdiSetBatchLimit(1)便于调试绘图代码;
画图代码闪烁的调试,
1.不适当的UpdateWindow调用,
2.调用InvalidateRect而不指定更新矩形,
3.调用InvalidateRect而将擦除背景参数不适当地设置为真,
4.不适当地使用CS_HREDRAW和CS_VREDRAW窗口风格,仅当客房区大小改变需要重画整个窗口时,才需要设置这两种风格。
如果窗口中的某些元素需要居中放置,这是必要的,
调试WM_MOUSEMOVE消息,大部分情况下,你希望在鼠标移动到窗口的特定位置或在特殊的环境下才发生中断,这时可以这样写
void CMyWnd::OnMouseMove(UINT nFlags, CPoint point)
{
#ifdef _DEBUG
if (GetAsyncKeyState(VF_CONTROL) < 0) {
int bogus = 0; // 可以在这里设置断点,仅当你按下Ctrl键时,才进入这里.
}
#endif
}
WM_LBUTTONDOWN 和 WM_LBUTTONUP 消息也是一个问题,因为在 WM_LBUTTONDOWN 消息处理函数中设置一个断点
很可能会导致 WM_LBUTTONUP 被调试器吸收.绕开这个问题的办法是在调试器中一直保持鼠标按下状态,只使用键盘控制调试器,
程序重新获得了输入焦点,你就可以释放鼠标按钮了。
使用 Spy++的 Log Messages 调试与消息有关的问题.
使用回调帮助调试代码,如调试工具提示时,使用 LPSTR_TEXTCALLBACK,
--------------------------------------------------------------------------------------------
某些变量应使用 volatile 避开编译器优化,使编译器产生的代码总是直接访问内存。
当前线程 ID, 在 Watch 窗口中输入 dw(@TIB+0x24)。
设置特定线程的断点,在关注的线程的 Watch 窗口中输入 @TIB 以确定线程的 TIB 地址,然后设置条件断点,@TIB == TIBAddress,TIBAddress是伪寄存器 @TIB 的值。
利用 TIB 的 pvArbitary 域设置了线程名称以后,可以在 Watch 窗口中输入 (PCHAR)(dw(@TIB+0x14))以显示当前线程的名称。
如果要单独调试某一线程,可以先在线程列表选择目标线程,再 SetFocus 这一线程,而后暂停所有其他线程,
VC调试技巧的更多相关文章
- Visual Studio原生开发的20条调试技巧(下)
我的上篇文章<Vistual Studio原生开发的10个调试技巧>引发了很多人的兴趣,所以我决定跟大家分享更多的调试技巧.接下来你又能看到一些对于原生应用程序的很有帮助的调试技巧(接着上 ...
- Visual Studio原生开发的10个调试技巧
这篇文章只介绍了一些有关Visual Studio的基本调试技巧,但是还有其他一些同样有用的技巧.我整理了一些Visual Studio(至少在VS 2008下)原生开发的调试技巧.(如果你是工作在托 ...
- Visual Studio原生开发的10个调试技巧(二)
来源:oschina 发布时间:2013-08-10 阅读次数:397 51 我以前关于Visual Studio调试技巧的文章引起了大家很大的兴趣,以至于我决定分享更多调试的知识.以下的列表中你 ...
- (转)Visual Studio原生开发的10个调试技巧(二)
我以前关于Visual Studio调试技巧的文章引起了大家很大的兴趣,以至于我决定分享更多调试的知识.以下的列表中你可以看到写原生开发的调试技巧(接着以前的文章来编号).这些技巧可以应用在VS200 ...
- Visual Studio原生开发的10个调试技巧(一)
最近碰巧读了Ivan Shcherbakov写的一篇文章,<11个强大的Visual Studio调试小技巧>.这篇文章只介绍了一些有关Visual Studio的基本调试技巧,但是还有其 ...
- 10个Visual Studio原生开发调试技巧
10个Visual Studio原生开发调试技巧(1) 2013-05-29 13:30 佚名 开源中国 我要评论(1) 字号:T | T 以下的列表中你可以看到写原生开发的调试技巧(接着以前的文章来 ...
- Visual Studio原生开发的10个调试技巧(转)
本文由 伯乐在线 - JingerJoe 翻译自 Marius Bancila.转载请参见文章末尾处的要求. [感谢@_La_Isla_Bonita 的热心翻译.如果其他朋友也有不错的原创或译文, ...
- VC调试篇
难怪很多前辈说调试是一个程序员最基本的技能,其重要性甚至超过学习一门语言.不会调试的程序员就意味着他即使会一门语言,却不能编制出任何好的软件. 我以前接触的程序大多是有比较成形的思路和方法,调试起来出 ...
- Visual Studio 原生开发的10个调试技巧(二)
原文:Visual Studio 原生开发的10个调试技巧(二) 我以前关于 Visual Studio 调试技巧的文章引起了大家很大的兴趣,以至于我决定分享更多调试的知识.以下的列表中你可以看到写原 ...
随机推荐
- 高级UIKit-05(CoreData)
[day06_1_CoreDataPerson]:保存person对象到coreData数据库 保存大量数据时用CoreData保存到数据库,数据库会存在documents目录下 操作步骤: 1.创建 ...
- EditText 软键盘
EditText 软键盘 package brother.eighteen.demoedittext; import android.content.Context; import android.t ...
- 一、Mongo的安装
注:学习为主,平台为WIN7 32位系统 一.Mongo的安装 1.1 下载 到官方下载地址(http://www.mongodb.org/downloads)去下载所需要的版本 1.2 安装与运行 ...
- poj 1990
题目链接 借鉴cxlove大神的思路 题意:听力v,位置x,2个牛交流声音为max(v1,v2)*(x1-x2),求总的 10000^2 tle 用的树状数组做的,排序,2个,小于vi的牛的总数和距离 ...
- 【剑指Offer学习】【面试题17 ::合并两个排序的链表】
题目:输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是依照递增排序的 链表结点定义例如以下: public static class ListNode { int value; List ...
- 记录Log4Net的使用
最近项目中有一个记录错误日志的功能模块,以前采用的是写TXT的做法.代码如下 /// <summary> /// 写入日志 /// </summary> public stat ...
- 超详细SDK Hello World
Windows应用程序的基本运行机制与HelloWin程序详细解 总的来说最基本的Windows应用程序的运行执行顺序总是以如下的基本顺序执行的. 顺序结构: 调用WinMain函数开始执行--à定义 ...
- ssh安装过程
1.在线安装[root@Asianux ~]# sudo apt-get install ssh 2.进行加密设置[root@Asianux ~]# ssh-keygen -t rsa3.启动SS ...
- Java 异常解决之java.lang.IllegalArgumentException: Comparison method violates its general contract!
Java 异常解决 在你的代码前加一句 System.setProperty("java.util.Arrays.useLegacyMergeSort", "true&q ...
- JENKINS 打包发布脚本
#!/bin/bash #nohup bash check_new_pkgs_dev.sh & #steps below: ##发布的机器上运行这个脚本 #定时遍历发布包存放路径 #1.遍历所 ...