對效能優化程式代碼進行偵錯
Microsoft 有某些技術可用來重新排列已編譯和鏈接的程式代碼,以便以更有效率執行。 這些技術會將記憶體階層的元件優化,並且以定型案例為基礎。
產生的優化可減少分頁(和頁面錯誤),並增加程式碼與數據之間的空間位置。 它解決了原始程序代碼位置不佳所導入的重要效能瓶頸。 經過此優化的元件可能會在函式內將其程式代碼或數據區塊移至二進位檔的不同位置。
在這些技術優化的模組中,程式代碼和數據區塊的位置通常位於與一般編譯和鏈接之後所在的位置不同的記憶體位址。 此外,函式可能已分割成許多非連續區塊,以便最常使用的程式代碼路徑可以位於相同頁面上彼此附近。
因此,函式(或任何符號)加上位移不一定具有相同的意義,在非優化程式代碼中。
對效能優化程式代碼進行偵錯
偵錯時,您可以在任何已載入符號的模組上使用 !lmi 擴充功能命令,查看模組是否已優化效能:
0:000> !lmi ntdll
Loaded Module Info: [ntdll]
Module: ntdll
Base Address: 77f80000
Image Name: ntdll.dll
Machine Type: 332 (I386)
Time Stamp: 394193d2 Fri Jun 09 18:03:14 2000
CheckSum: 861b1
Characteristics: 230e stripped perf
Debug Data Dirs: Type Size VA Pointer
MISC 110, 0, 76c00 [Data not mapped]
Image Type: DBG - Image read successfully from symbol server.
c:\symbols\dll\ntdll.dbg
Symbol Type: DIA PDB - Symbols loaded successfully from symbol server.
c:\symbols\dll\ntdll.pdb
在此輸出中,請注意「特性」行上的字詞 效能 。 這表示此效能優化已套用至ntdll.dll。
調試程式能夠了解沒有位移的函式或其他符號;這可讓您在函式或其他標籤上設定斷點,而沒有任何問題。 不過,反組譯碼作業的輸出可能會造成混淆,因為此反組譯碼會反映優化器所做的變更。
由於調試程式會嘗試保持接近原始程序代碼,您可能會看到一些有趣的結果。 使用效能優化的程式代碼時,經驗法則只是您無法在優化程式代碼上執行可靠的位址算術。
以下是範例:
kd> bl
0 e f8640ca6 0001 (0001) tcpip!IPTransmit
1 e f8672660 0001 (0001) tcpip!IPFragment
kd> u f864b4cb
tcpip!IPTransmit+e48:
f864b4cb f3a4 rep movsb
f864b4cd 8b75cc mov esi,[ebp-0x34]
f864b4d0 8b4d10 mov ecx,[ebp+0x10]
f864b4d3 8b7da4 mov edi,[ebp-0x5c]
f864b4d6 8bc6 mov eax,esi
f864b4d8 6a10 push 0x10
f864b4da 034114 add eax,[ecx+0x14]
f864b4dd 57 push edi
您可以從斷點清單中看到IPTransmit的位址0xF8640CA6。
當您在0xF864B4CB取消組譯此函式內程式代碼的區段時,輸出會指出這是超過函式開頭的位元組0xE48位元組。 不過,如果您從這個位址減去函式的基底,實際位移似乎0xA825。
發生的情況是:調試程式確實顯示從0xF864B4CB開始的二進位指令反組譯碼。 但是調試程式會以簡單的減法計算位移,而是在執行優化之前,顯示函式專案的位移,如同原始程序代碼中存在的位移一樣。 該值0xE48。
另一方面,如果您嘗試查看 IPTransmit+0xE48,您會看到:
kd> u tcpip!iptransmit+e48
tcpip!ARPTransmit+d8:
f8641aee 0856ff or [esi-0x1],dl
f8641af1 75fc jnz tcpip!ARPTransmit+0xd9 (f8641aef)
f8641af3 57 push edi
f8641af4 e828eeffff call tcpip!ARPSendData (f8640921)
f8641af9 5f pop edi
f8641afa 5e pop esi
f8641afb 5b pop ebx
f8641afc c9 leave
此處的情況是調試程式會將符號 IPTransmit 辨識為相當於位址0xF8640CA6,而命令剖析器會執行簡單的新增功能,以尋找該0xF8640CA6 + 0xE48 = 0xF8641AEE。 接著,此位址會作為 u (Unassemble) 命令的自變數。 但是一旦分析這個位置,調試程式就會發現這不是 IPTransmit ,加上0xE48的位移。 事實上,這根本不是這個函式的一部分。 相反地,它會對應至 函式 ARPTransmit 加上0xD8的位移。
發生這種情況的原因是效能優化無法透過位址算術來反轉。 雖然調試程式可以擷取位址並推算其原始符號和位移,但沒有足夠的資訊可取得符號和位移,並將其轉譯為正確的位址。 因此,反組譯碼在這些情況下並無用處。