VS編譯器優化誘發一個的Bug

VS編譯器優化誘發一個的Bug

Bug的背景

我正在把某個C++下的驅動程序移植到C下,前幾天發生了一個比較詭異的問題。安全

驅動程序有一個bug,可是這個bug只能 Win32 Release 版本下的驅動才能重現。在 Win32 Debug 版本下,和 Win64 Release/Debug 版本下均沒法從新。多線程

隨着一步步的分析,最終發現問題是因爲VS編譯器的一個優化誘發的。固然這並非VS編譯器的bug,只是因爲優化誘發程序裏面的某個bug.app

調試的思路

1. Debug Vs Release

首先想到的天然是看看Debug版本和Release版本運行是有啥區別了。Release版本本質上和Debug版本沒啥區別,同樣均可以使用調試器調試,只不過有些大大小小須要注意的地方罷了。基本上熟知編譯器的優化原理和調試器,調試Release版本的程序也不是啥困難的事情。函數

通常致使Debug和Release不一樣的常見問題無非下面這些,未初始化的局部變量,程序指針訪問越界,多線程同步問題。而這些問題都是很容易發現的,但隨着調試的深刻,並無發現這些問題的蹤影,反而全部的代碼都工做的很好,沒啥異常發生。對比Debug和Release版本下的流程,Release版本的問題在於在某個時候硬件沒有如期的觸發中斷。但在這以前兩個版本所執行的代碼邏輯徹底同樣,沒有啥區別。優化

問題愈加詭異了。線程

2. 32bit Vs 64 bit

既然64bit Release的版本沒有問題,但32bit Release有問題,這也是一個突破的思路。debug

把相關的代碼拿出來仔細梳理,注意看一些32/64bit下的常見問題,諸如指針大小,整數溢出等問題。不過依然沒有發現問題。指針

3. 編譯器的優化

Debug和Release的最大的區別天然是編譯器的優化不一樣,可是哪一個編譯器不是久經考驗,要是說編譯器上出問題,那真是撞了大運了,因此沒有第一時間考慮是因爲優化形成的。不過既然直接調試代碼沒發現問題的所在,那只有使用另一種分析方法了,那就是不斷縮小致使問題的範圍。調試

既然我手上有兩個版本,一個好的,一個壞的。那隻要不斷縮小兩個版本之間的不一樣,最終就能定位到致使問題的部分。code

因此第一個拿來開刀的天然是編譯器的優化選項。幾經嘗試,終於發現誘發問題的編譯器優化是這一項,Favor Size Or Speed. 詭異,真是至關的詭異呀。不過無所謂,既然找到了這一個線索,下面就好找了。這個優化只是一個函數內的局部優化,只要針對不一樣的函數分別打開和關閉這個優化,天然就能發現問題所在了。

問題的根源

針對一些可疑的代碼,分別打開和關閉這個Favor Size Or Speed的優化,不斷的縮小範圍以後,最終定位到某個函數是罪魁禍首。而後比較了一下生成的彙編代碼,最後定位到了下面這行語句

pDevice->pReg->S = 0xFFFFFFFF;

而這行代碼生成的彙編代碼以下:

or      dword ptr [eax+10h],0FFFFFFFFh   //Release版本,Favor Size優化
mov     dword ptr [eax+10h],0FFFFFFFFh   //Debug版本,無優化

乍一看必定會以爲這個詭異,由於or和mov指令在這裏的邏輯實際上是如出一轍的,編譯器沒有任何錯誤。那爲什麼會有不一樣的執行效果呢?

其實緣由在於pDevice->pReg->S並非一個普通的內存地址,他是一個MMIO(memory mapped IO) address地址。也就是說這個變量並不存在於內存裏面,而是硬件的某個寄存器,只不過和內存共用了一個地址空間。而對於這種MMIO address,雖然能夠直接象訪問普通內存同樣讀寫他們,但最安全的方式仍是使用Windows提供的函數,因此對於這個bug的修復是

WRITE_REGISTER_ULONG(&pDevice->pReg->S,0xFFFFFFFF);

至於爲啥會出這種烏龍,其實就是一開始移植的時候粗枝大葉了。pDevice->pReg->S = 0xFFFFFFFF; 這句C++語句中的=其實一個重載過的C++運算符,內部的實現就是WRITE_REGISTER_ULONG,移植過來的時候也沒有細看具體哪一個S是個什麼東西,就抄過來了

相關文章
相關標籤/搜索