神秘的 _DEBUG 宏从何处来?
原调试debugwindbgvsdllcrash崩溃全局变量
缘起
在上一篇文章 《调试实战 —— dll 加载失败之Debug Release争锋篇》中,由于两个工程中的 _ITERATOR_DEBUG_LEVEL 不同,导致了对同一个 map 的解析不同,从而导致了崩溃。在示例代码中,我是手动更改的该宏的值,在实际工程中,却另有玄机。在上文中故意省略了这部分内容的介绍。现把实际工程的问题在本文中做个相对详细的梳理总结。
先剧透一下:实际工程中的问题是因为一个工程中定义了 _DEBUG 宏,另外一个工程里没定义。但是我已经核对过,两个工程都没定义 _DEBUG 宏。其中一个工程的 _DEBUG 宏是从哪儿来的呢?
测试工程简介
为了查出 _DEBUG 宏从何而来,我特意建了一个超级简单的工程。只包含一个源文件,其内容如下:
#ifdef _DEBUG
#pragma message("---- _DEBUG defined.")
#else
#pragma message("---- _DEBUG NOT defined.")
#endif
int wmain(){
return 0;
}
相信大家都知道,debug 会定义 _DEBUG 宏,而 release 不会定义 _DEBUG 宏。默认的 debug 和 release 中对应的 Preprocessor definition 配置对比如下图:
preprocessor-definitions-comparation
最开始,我以为简单的删掉 _DEBUG 宏,编译的时候就不会有 _DEBUG 宏了。
删除 _DEBUG 宏
没想到……
顽强的 _DEBUG 宏
再次编译的时候, _DEBUG 宏还是被定义了。
compile-result
误入歧途
根据经验,工程配置可以直接存储在工程文件(后缀一般是 .vcxproj),也可以存储在 .props 文件中。在 TestDebugMacro.vcxproj 中搜索 _DEBUG 宏,一无所获!会不会存储在 .props 中呢?使用 File Locator 搜索关键字 _DEBUG,搜索条件如下图:
search-_DEBUG-macro-in-props
只搜到了几条相关的记录,因为使用的是 vs2013,对应的版本号是 v120,所以只需要关注高亮的搜索记录。
虽然,看上去不太可能,但是抱着试试看的心态,删除 Microsoft.Cpp.AppContainerApplication.props 第 104 行的 _DEBUG 宏,
重新编译。结果, _DEBUG 宏,依然顽强的活着。这里就不截图了。
会不会记录在注册表里呢?搜索一番,一无所获!
肯定是记录到哪里了,应该不会动态生成吧?!
继续搜索
扩大搜索范围,在所有文件中搜索,经过排查,只有 cl.exe 中的记录比较靠谱。
search-_DEBUG
难道写死在 cl.exe 中了?有点儿不可思议。(文中暗表,确实写死在 cl.exe 中了。)
顺着这条路已经找不到更多有价值的线索了,需要换个思路了。
换个思路
会不会是 vs 在编译的时候,根据某些条件自动添加的?为了排除这种情况,直接使用 msbuild.exe 进行构建操作(在 《全局变量初始化顺序探究》里已经介绍过了)。
build-with-msbuild
可以发现,直接使用 msbuild.exe 编译也会定义 _DEBUG 宏。可以把 vs 从怀疑名单中划掉了。
注意看传递给 cl.exe 的参数(上图黄色高亮部分)中也没有 _DEBUG 宏的踪影。
难道是 msbuild 通过进程通信手段实现的?(真佩服自己的脑洞)
试试直接使用 cl.exe TestDebugMacro.cpp 编译。
柳暗花明
居然 _DEBUG 宏消失了!!!
compile-with-cl
通过 msbuild.exe 启动的 cl.exe ,从日志可以发现 cl.exe 的参数如下:
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\CL.exe /c /ZI /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _CONSOLE /D _LIB
/D _UNICODE /D UNICODE /Gm /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Fo"Debug\\" /Fd"Debug\vc120.pdb" /Gd /TP
/analyze- /errorReport:queue TestDebugMacro.cpp
而手动执行的 cl.exe 只传递了需要编译的文件名。会不会是哪个神奇的参数搞的鬼呢?好办,二分法排查!
经过几次尝试,很快定位到是 /MDd 搞得鬼!
compare-cl-param
/MDd 选项是何方神圣?搜索一下,很快找到了微软官方的介绍。
/MDd 选项
官方文档很明确的描述到:/MDd 选项会定义 _DEBUG,_MT 和 _DLL ,并且会使用调试 版本的多线程运行时库。
具体请介绍参考微软官方文档截图:
mdd-option-msdn
贴一张工程属性设置截图。
set-mdd-option-in-vs
反思
当时没有严格按照对比的思路进行排查,浪费了很多时间!
很久之前确实对比过 /MDd 几种选项的不同,当时的关注点主要在于会链接不同的运行时库,忽略了对宏的影响。相信经过这次折腾,我永远也忘不了 /MDd 选项定义 _DEBUG 宏。这个行为不是通过配置文件发生的,而是写到了 cl.exe 的文件中!
总结
File Locator 真可谓文件内容搜索神器,经常排错的小伙伴儿必备!
一种情况是正常,一种情况不正常,最简单粗暴有效的办法就是对比!
排查问题时,我们要尽量简化问题,尽可能排除无关条件的干扰。
/MDd 选项不仅会影响链接库,还会定义 _DEBUG 宏。
参考资料