[心得] 如何造成程式 Crash
最近在改程式的時候,發生以下異常存取的狀況
原因是以下的指標指到不合法的記憶體,但查很久就是查不到在那裡被改的
後來採用 Data Breakpoint 的方式,先在指標建立的地方設定中斷點,然後在建立後取得其指標變數的位址設定 Data Breakpoint,因此當該位址的資料變化時就會產生中斷。指令為
DEBUG | New Breakpoint | New Data Breakpoint
通常中斷後 Keyboard 幾乎無法操作 (控制權被 Debug 搶走了),所以我在後面加了一行 TRACE 的指令,執行後用滑鼠選取變數名稱的位址,開啟 Data Breakpoint 視窗後會自動帶入 Address 欄位,只要再把 Byte Count 改成 4 即可 (Win32 point 佔 4 Bytes)。繼續往下執行即會產生下列中斷
中斷後有問題的程式碼
再看一下變數的宣告
以及 TempId 的宣告
原來 m_Temperature 只有宣告 9 個 INT 的空間,但 TempId 因為其他原因改成 10 個,且未同步更新 m_Temperature 的宣告,所以第 10 個就不小心蓋到 m_pStaticCoreVoltage 指標造成程式異常結束了
但奇怪的是,這一段程式只有在 Win32 下有問題,x64 並未發生問題,原來是 m_pStaticCoreVoltage 前面有 4 Bytes padding (可能是 x64 Pointer 為 8 Bytes,為了 Performance 的關係有作 Align 的調整),所以錯誤的資料沒有蓋到該指標變數
結論:
原因是以下的指標指到不合法的記憶體,但查很久就是查不到在那裡被改的
if (m_pStaticCoreVoltage->IsWindowVisible())
{
m_pStaticCoreVoltage->SetWindowText(m_strMonCoreVoltage);
}
後來採用 Data Breakpoint 的方式,先在指標建立的地方設定中斷點,然後在建立後取得其指標變數的位址設定 Data Breakpoint,因此當該位址的資料變化時就會產生中斷。指令為
DEBUG | New Breakpoint | New Data Breakpoint
通常中斷後 Keyboard 幾乎無法操作 (控制權被 Debug 搶走了),所以我在後面加了一行 TRACE 的指令,執行後用滑鼠選取變數名稱的位址,開啟 Data Breakpoint 視窗後會自動帶入 Address 欄位,只要再把 Byte Count 改成 4 即可 (Win32 point 佔 4 Bytes)。繼續往下執行即會產生下列中斷
中斷後有問題的程式碼
for (INT i = 0; i < _countof(TempId); i++) { PIObservable pObj = m_pMonitorCtrl->RegistryMonitor(this, TempId[i], nGpuIndex + 1, bRegister); if (bRegister && pObj) { m_nTemperature[i] = (INT)pObj->getCur(); } }
再看一下變數的宣告
INT m_nTemperature[9]; // GPU x 1, Power x 5, Memory x 3 CWnd* m_pStaticCoreVoltage;
以及 TempId 的宣告
const MonitorId TempId[] =
{
ID_THERMAL, // GPU
ID_GPU_TEMP2,
ID_MEM_TEMP1, // Memory
ID_MEM_TEMP2,
ID_MEM_TEMP3,
ID_POWER_TEMP1, // Power
ID_POWER_TEMP2,
ID_POWER_TEMP3,
ID_POWER_TEMP4,
ID_POWER_TEMP5,
};
原來 m_Temperature 只有宣告 9 個 INT 的空間,但 TempId 因為其他原因改成 10 個,且未同步更新 m_Temperature 的宣告,所以第 10 個就不小心蓋到 m_pStaticCoreVoltage 指標造成程式異常結束了
但奇怪的是,這一段程式只有在 Win32 下有問題,x64 並未發生問題,原來是 m_pStaticCoreVoltage 前面有 4 Bytes padding (可能是 x64 Pointer 為 8 Bytes,為了 Performance 的關係有作 Align 的調整),所以錯誤的資料沒有蓋到該指標變數
Win32 Memory
x64 Memory
結論:
- 程式中不要使用靜態的陣列宣告,改用 STL vector 取代
vector<int> m_nTemperatureVector; // GPU x 2, Power x 5, Memory x 3 for (INT i = 0; i < _countof(TempId); i++) { PIObservable pObj = m_pMonitorCtrl->RegistryMonitor(this, TempId[i], nGpuIndex + 1, bRegister); if (bRegister && pObj) { m_nTemperatureVector.push_back(pObj->getCur()); } }
- 存取陣列元素一定要做邊界檢查 (即使是 vector 也是會產生邊界異常)
留言