Bagaimana cara kerja debugger?

170

Saya terus bertanya-tanya bagaimana cara kerja debugger? Khususnya yang bisa 'dilampirkan' agar sudah bisa dieksekusi. Saya mengerti bahwa kompiler menerjemahkan kode ke bahasa mesin, tetapi kemudian bagaimana debugger 'tahu' apa yang dilampirkan?

ya23
sumber
5
Artikel Eli telah pindah ke eli.thegreenplace.net/2011/01/23/how-debuggers-work-part-1
Oktalist
@Oktalist Artikel ini menarik tetapi hanya berbicara tentang abstraksi tingkat API untuk debugging di Linux. Saya kira OP ingin tahu lebih banyak tentang di balik tenda.
smwikipedia

Jawaban:

96

Detail tentang cara kerja debugger akan tergantung pada apa yang Anda debug, dan apa OS-nya. Untuk debugging asli di Windows Anda dapat menemukan beberapa detail di MSDN: Win32 Debugging API .

Pengguna memberi tahu debugger proses mana yang harus dilampirkan, baik dengan nama atau ID proses. Jika itu adalah nama maka debugger akan mencari proses ID, dan memulai sesi debug melalui panggilan sistem; di bawah Windows ini akan menjadi DebugActiveProcess .

Setelah dilampirkan, debugger akan memasuki loop peristiwa seperti pada UI apa pun, tetapi alih-alih peristiwa yang berasal dari sistem windowing, OS akan menghasilkan peristiwa berdasarkan apa yang terjadi dalam proses debugged - misalnya pengecualian yang terjadi. Lihat WaitForDebugEvent .

Debugger dapat membaca dan menulis memori virtual proses target, dan bahkan menyesuaikan nilai registernya melalui API yang disediakan oleh OS. Lihat daftar fungsi debugging untuk Windows.

Debugger dapat menggunakan informasi dari file simbol untuk menerjemahkan dari alamat ke nama variabel dan lokasi dalam kode sumber. Informasi file simbol adalah kumpulan API yang terpisah dan bukan merupakan bagian inti dari OS. Pada Windows ini melalui SDK Akses Antarmuka Debug .

Jika Anda men-debug lingkungan yang dikelola (.NET, Java, dll.) Proses biasanya akan terlihat serupa, tetapi detailnya berbeda, karena lingkungan mesin virtual menyediakan API debug daripada OS yang mendasarinya.

Rob Walker
sumber
5
Pertanyaan ini mungkin terdengar bodoh tetapi bagaimana OS melacak jika alamat tertentu di dalam program tercapai. Misalnya, breackpoint diatur pada alamat 0x7710cafe. Ketika pointer instruksi mengubah OS (atau mungkin CPU) harus membandingkan pointer instruksi dengan semua alamat break point, atau saya salah? Bagaimana cara kerjanya ..?
displayname
3
@StefanFalk Saya menulis jawaban yang membahas beberapa detail level bawah (pada x86).
Jonathon Reinhart
Bisakah Anda menjelaskan bagaimana tepatnya nama variabel dipetakan ke alamat? Apakah aplikasi menggunakan alamat memori yang sama untuk variabel setiap kali dijalankan? Saya selalu berasumsi bahwa itu baru saja ditemukan dipetakan dari memori yang tersedia tetapi tidak pernah benar-benar berpikir tentang apakah byte akan memetakan langsung ke tempat yang sama di ruang memori aplikasi. Sepertinya itu akan menjadi masalah keamanan utama.
James Joshua Street
@JamesJoshuaStreet Saya akan membayangkan bahwa itu adalah detail yang khusus untuk debugger.
moonman239
Jawaban ini mengungkapkan sesuatu. Tapi saya pikir op lebih tertarik pada beberapa detail level rendah daripada beberapa abstraksi API.
smwikipedia
63

Seperti yang saya pahami:

Untuk breakpoint perangkat lunak pada x86, debugger menggantikan byte pertama dari instruksi dengan CC ( int3). Ini dilakukan dengan WriteProcessMemorydi Windows. Ketika CPU sampai ke instruksi itu, dan mengeksekusi int3, ini menyebabkan CPU untuk menghasilkan pengecualian debug. OS menerima interupsi ini, menyadari bahwa proses sedang di-debug, dan memberi tahu proses debugger bahwa breakpoint terkena.

Setelah breakpoint dipukul dan proses dihentikan, debugger melihat daftar breakpointnya, dan menggantikannya CCdengan byte yang awalnya ada di sana. Set debugger TF, Bendera Perangkap dalam EFLAGS(dengan memodifikasi CONTEXT), dan melanjutkan proses. Bendera Perangkap menyebabkan CPU secara otomatis menghasilkan pengecualian satu langkah ( INT 1) pada instruksi berikutnya.

Ketika proses yang sedang di-debug berhenti pada waktu berikutnya, debugger akan kembali menggantikan byte pertama dari instruksi breakpoint CC, dan proses berlanjut.

Saya tidak yakin apakah ini persis seperti yang diterapkan oleh semua debugger, tapi saya telah menulis program Win32 yang berhasil men-debug sendiri menggunakan mekanisme ini. Sama sekali tidak berguna, tetapi mendidik.

Jonathon Reinhart
sumber
25

Di Linux, proses debugging dimulai dengan panggilan sistem ptrace (2) . Artikel ini memiliki tutorial yang bagus tentang cara menggunakan ptraceuntuk menerapkan beberapa konstruksi debugging sederhana.

Adam Rosenfield
sumber
1
Apakah informasi tersebut (2)memberi tahu kami sesuatu yang lebih (atau kurang) dari "ptrace adalah panggilan sistem"?
Lazer
5
@ eSKay, tidak, tidak juga. Ini (2)adalah nomor bagian manual. Lihat en.wikipedia.org/wiki/Man_page#Manual_sections untuk deskripsi bagian manual.
Adam Rosenfield
2
@AdamRosenfield Kecuali untuk fakta bahwa bagian 2 secara khusus "Panggilan Sistem". Jadi secara tidak langsung, ya, itu memberitahu kita bahwa itu ptraceadalah panggilan sistem.
Jonathon Reinhart
1
Secara lebih praktis, ini (2)memberitahu kita bahwa kita dapat mengetik man 2 ptracedan mendapatkan halaman buku yang tepat - tidak penting di sini karena tidak ada yang lain ptraceuntuk disatukan, tetapi untuk dibandingkan man printfdengan man 3 printfdi Linux.
langsing
9

Jika Anda menggunakan OS Windows, sumber yang bagus untuk ini adalah "Aplikasi Debugging untuk Microsoft .NET dan Microsoft Windows" oleh John Robbins:

(atau bahkan edisi yang lebih lama: "Aplikasi Debugging" )

Buku ini memiliki bab tentang cara kerja debugger yang menyertakan kode untuk beberapa debugger sederhana (tetapi berfungsi).

Karena saya tidak terbiasa dengan rincian debugging Unix / Linux, hal ini mungkin tidak berlaku sama sekali untuk OS lain. Tapi saya kira itu sebagai pengantar untuk subjek yang sangat kompleks konsep - jika bukan detail dan API - harus 'port' ke sebagian besar OS.

Michael Burr
sumber
3

Sumber berharga lainnya untuk memahami debugging adalah manual CPU Intel (Manual Pengembang Perangkat Lunak Intel® 64 dan IA-32). Dalam volume 3A, bab 16, ia memperkenalkan dukungan perangkat keras untuk debugging, seperti pengecualian khusus dan register debugging perangkat keras. Berikut ini adalah dari bab itu:

Bendera T (perangkap), TSS - Menghasilkan pengecualian debug (#DB) ketika upaya dilakukan untuk beralih ke tugas dengan bendera T yang diatur di TSS-nya.

Saya tidak yakin apakah Window atau Linux menggunakan flag ini atau tidak, tetapi sangat menarik untuk membaca bab itu.

Semoga ini bisa membantu seseorang.

Jiang
sumber
2

Saya pikir ada dua pertanyaan utama untuk dijawab di sini:

1. Bagaimana debugger mengetahui bahwa pengecualian terjadi?

Ketika pengecualian terjadi dalam proses yang sedang di-debug, debugger akan diberitahukan oleh OS sebelum penangan pengecualian pengguna yang didefinisikan dalam proses target diberi kesempatan untuk merespons pengecualian tersebut. Jika debugger memilih untuk tidak menangani pemberitahuan pengecualian (kesempatan pertama) ini, urutan pengiriman pengecualian berlanjut lebih jauh dan utas target kemudian diberi kesempatan untuk menangani pengecualian jika ingin melakukannya. Jika pengecualian SEH tidak ditangani oleh proses target, debugger kemudian dikirim acara debug lain, disebut pemberitahuan kesempatan kedua, untuk menginformasikan bahwa pengecualian tidak tertangani terjadi dalam proses target. Sumber

masukkan deskripsi gambar di sini


2. Bagaimana debugger tahu cara berhenti di breakpoint?

Jawaban yang disederhanakan adalah: Ketika Anda memasukkan break-point ke dalam program, debugger mengganti kode Anda pada saat itu dengan instruksi int3 yang merupakan interupsi perangkat lunak . Akibatnya, program ditangguhkan dan debugger dipanggil.

InTheNameOfScience
sumber
1

Pemahaman saya adalah bahwa ketika Anda mengkompilasi aplikasi atau file DLL, apa pun yang dikompilasi berisi simbol yang mewakili fungsi dan variabel.

Ketika Anda memiliki build debug, simbol-simbol ini jauh lebih rinci daripada ketika build rilis, sehingga memungkinkan debugger memberi Anda lebih banyak informasi. Saat Anda melampirkan debugger ke suatu proses, ia melihat fungsi mana yang saat ini sedang diakses dan menyelesaikan semua simbol debug yang tersedia dari sini (karena ia tahu seperti apa internal dari file yang dikompilasi, ia dapat memastikan apa yang mungkin ada dalam memori. , dengan isi int, float, string, dll.). Seperti yang dikatakan poster pertama, informasi ini dan cara kerja simbol-simbol ini sangat tergantung pada lingkungan dan bahasa.

DavidG
sumber
2
Ini hanya tentang simbol. Ada jauh, jauh lebih banyak untuk debugging daripada simbol.
Jonathon Reinhart