Bagikan melalui


Mendebug Kode yang Telah Dioptimalkan untuk Performa

Microsoft memiliki teknik tertentu yang digunakannya untuk mengatur ulang kode yang dikompilasi dan ditautkan sehingga dijalankan dengan efisiensi yang lebih tinggi. Teknik ini mengoptimalkan komponen untuk hierarki memori, dan didasarkan pada skenario pelatihan.

Pengoptimalan yang dihasilkan mengurangi halaman (dan kesalahan halaman), dan meningkatkan lokalitas spasial antara kode dan data. Ini mengatasi hambatan performa utama yang disebabkan oleh posisi kode yang buruk. Komponen yang telah melalui pengoptimalan ini mungkin memiliki kode atau blok datanya dalam fungsi yang dipindahkan ke lokasi biner yang berbeda.

Dalam modul yang telah dioptimalkan oleh teknik ini, lokasi kode dan blok data akan sering ditemukan di alamat memori yang berbeda dari lokasi tempat mereka akan berada setelah kompilasi dan penautan normal. Selain itu, fungsi mungkin telah dibagi menjadi banyak blok yang tidak berdekatan, agar jalur kode yang paling umum digunakan dapat ditemukan dekat satu sama lain di halaman yang sama.

Oleh karena itu, fungsi (atau simbol apa pun) ditambah offset tidak akan selalu memiliki arti yang sama yang akan dimilikinya dalam kode yang tidak dioptimalkan.

Debugging kode Performance-Optimized

Saat penelusuran kesalahan, Anda dapat melihat apakah modul telah dioptimalkan performanya dengan menggunakan perintah ekstensi !lmi pada modul apa pun yang simbolnya telah dimuat:

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

Dalam output ini, perhatikan istilah perf pada baris "Karakteristik". Ini menunjukkan bahwa pengoptimalan performa ini telah diterapkan ke ntdll.dll.

Debugger dapat memahami fungsi atau simbol lain tanpa offset; ini memungkinkan Anda untuk mengatur titik henti pada fungsi atau label lain tanpa masalah. Namun, output operasi pembongkaran mungkin membingungkan, karena pembongkaran ini akan mencerminkan perubahan yang dilakukan oleh pengoptimal.

Karena debugger akan mencoba untuk tetap dekat dengan kode asli, Anda mungkin melihat beberapa hasil yang lucu. Aturan praktis saat bekerja dengan kode yang telah dioptimalkan untuk performa adalah Anda tidak dapat melakukan perhitungan alamat yang dapat diandalkan pada kode yang dioptimalkan.

Berikut adalah contohnya:

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

Anda dapat melihat dari daftar titik henti bahwa alamat IPTransmit adalah 0xF8640CA6.

Saat Anda menghapus seksi kode dalam fungsi ini pada 0xF864B4CB, output menunjukkan bahwa ini 0xE48 byte melewati awal fungsi. Namun, jika Anda mengurangi basis fungsi dari alamat ini, offset aktual tampaknya 0xA825.

Yang terjadi adalah: Debugger memang menunjukkan pembongkaran instruksi biner yang dimulai pada 0xF864B4CB. Tetapi alih-alih menghitung offset dengan pengurangan sederhana, debugger menampilkan -- sebaik mungkin -- offset ke entri fungsi seperti yang ada dalam kode asli sebelum pengoptimalan dilakukan. Nilai tersebut 0xE48.

Di sisi lain, jika Anda mencoba melihat IPTransmit+0xE48, Anda akan melihat ini:

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

Apa yang terjadi di sini adalah bahwa debugger mengenali simbol IPTransmit setara dengan alamat 0xF8640CA6, dan pengurai perintah melakukan tambahan sederhana untuk menemukan bahwa 0xF8640CA6 + 0xE48 = 0xF8641AEE. Alamat ini kemudian digunakan sebagai argumen untuk perintah u (Unassemble). Tetapi setelah lokasi ini dianalisis, debugger menemukan bahwa ini bukan IPTransmit ditambah offset 0xE48. Memang, itu bukan bagian dari fungsi ini sama sekali. Sebaliknya, ini sesuai dengan fungsi ARPTransmit ditambah offset 0xD8.

Alasan ini terjadi adalah bahwa pengoptimalan performa tidak dapat dibalik melalui aritmatika alamat. Meskipun debugger dapat mengambil alamat dan menyimpulkan simbol serta offset aslinya, debugger tersebut tidak memiliki cukup informasi untuk mengambil simbol dan offset tersebut dan menerjemahkannya ke alamat yang benar. Akibatnya, pembongkaran tidak berguna dalam kasus ini.