缘起
在上一篇文章 《调试实战 —— 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
配置对比如下图:
最开始,我以为简单的删掉 _DEBUG
宏,编译的时候就不会有 _DEBUG
宏了。
没想到……
顽强的 _DEBUG 宏
再次编译的时候, _DEBUG
宏还是被定义了。
误入歧途
根据经验,工程配置可以直接存储在工程文件(后缀一般是 .vcxproj
),也可以存储在 .props
文件中。在 TestDebugMacro.vcxproj
中搜索 _DEBUG
宏,一无所获!会不会存储在 .props
中呢?使用 File Locator
搜索关键字 _DEBUG
,搜索条件如下图:
只搜到了几条相关的记录,因为使用的是 vs2013
,对应的版本号是 v120
,所以只需要关注高亮的搜索记录。
虽然,看上去不太可能,但是抱着试试看的心态,删除 Microsoft.Cpp.AppContainerApplication.props
第 104
行的 _DEBUG
宏,
重新编译。结果, _DEBUG
宏,依然顽强的活着。这里就不截图了。
会不会记录在注册表里呢?搜索一番,一无所获!
肯定是记录到哪里了,应该不会动态生成吧?!
继续搜索
扩大搜索范围,在所有文件中搜索,经过排查,只有 cl.exe
中的记录比较靠谱。
难道写死在 cl.exe
中了?有点儿不可思议。(文中暗表,确实写死在 cl.exe
中了。)
顺着这条路已经找不到更多有价值的线索了,需要换个思路了。
换个思路
会不会是 vs
在编译的时候,根据某些条件自动添加的?为了排除这种情况,直接使用 msbuild.exe
进行构建操作(在 《全局变量初始化顺序探究》里已经介绍过了)。
可以发现,直接使用 msbuild.exe
编译也会定义 _DEBUG
宏。可以把 vs
从怀疑名单中划掉了。
注意看传递给 cl.exe
的参数(上图黄色高亮部分)中也没有 _DEBUG
宏的踪影。
难道是 msbuild
通过进程通信手段实现的?(真佩服自己的脑洞)
试试直接使用 cl.exe TestDebugMacro.cpp
编译。
柳暗花明
居然 _DEBUG
宏消失了!!!
通过 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
搞得鬼!
/MDd
选项是何方神圣?搜索一下,很快找到了微软官方的介绍。
/MDd 选项
官方文档很明确的描述到:/MDd
选项会定义 _DEBUG
,_MT
和 _DLL
,并且会使用调试 版本的多线程运行时库。
具体请介绍参考微软官方文档截图:
贴一张工程属性设置截图。
反思
当时没有严格按照对比的思路进行排查,浪费了很多时间!
很久之前确实对比过 /MDd
几种选项的不同,当时的关注点主要在于会链接不同的运行时库,忽略了对宏的影响。相信经过这次折腾,我永远也忘不了 /MDd
选项定义 _DEBUG
宏。这个行为不是通过配置文件发生的,而是写到了 cl.exe
的文件中!
总结
File Locator
真可谓文件内容搜索神器,经常排错的小伙伴儿必备!- 一种情况是正常,一种情况不正常,最简单粗暴有效的办法就是对比!
- 排查问题时,我们要尽量简化问题,尽可能排除无关条件的干扰。
/MDd
选项不仅会影响链接库,还会定义_DEBUG
宏。
参考资料
https://docs.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library?view=vs-2019