Bagaimana cara GDB menghentikan eksekusi

16

Seperti yang Anda ketahui, kami dapat menggunakan GDB dan mengatur breakpoint pada kode kami untuk menghentikan sementara eksekusi untuk debugging.

Pertanyaan saya adalah, bagaimana GDB menghentikan proses dan membiarkan Anda melihat konten register menggunakan i rmisalnya. Bukankah register itu digunakan oleh proses OS lain secara konstan? bagaimana mereka tidak ditimpa?

Apakah ini hanya snapshot dari konten dan bukan data langsung?

Joe
sumber
2
Kenapa semua register tidak ditimpa ketika OS memutuskan untuk menghentikan sementara program Anda dan menjalankan yang lain?
user253751
CppCon 2018: Simon Merek "Bagaimana C + + Debuggers Bekerja" youtube.com/watch?v=0DDrseUomfU
Robert Andrzantai

Jawaban:

24

Ini sedikit berbeda dengan arsitektur, tetapi poin-poin penting berlaku hampir secara universal:

  • Servis interupsi menyebabkan keadaan CPU (termasuk register) disimpan ke memori sebelum menjalankan ISR, dan dipulihkan saat ISR keluar.

  • Jika rutinitas layanan interupsi menukar konten lokasi memori tempat register itu disimpan, ia dapat melakukan pengalih konteks . Setiap utas memiliki wilayah memori tempat registernya disimpan saat utas tidak berjalan.

  • Switch konteks dikontrol oleh penjadwal ulir yang memperhitungkan apakah utas menunggu I / O, sinkronisasi, prioritasnya, pengiriman sinyal, dll. Seringkali ada penghitungan sementara yang diperhitungkan.

  • Debugger dapat menambah jumlah penangguhan, yang menjamin utas tidak dapat dijalankan. Kemudian ia dapat memeriksa (dan mengubah) salinan register yang tersimpan di thread.

Ben Voigt
sumber
14

Selain informasi hebat oleh @BenVoigt, izinkan saya untuk membuat beberapa tambahan:

Breakpoint diatur oleh debugger dengan mengganti nilai kode mesin (instruksi atau bagian dari instruksi) dalam proses yang sedang dibebani dengan instruksi trap tertentu di lokasi dalam kode yang sesuai dengan jalur (sumber) yang diinginkan untuk dihancurkan. Instruksi perangkap khusus ini dimaksudkan untuk digunakan sebagai breakpoint - debugger mengetahui hal ini dan begitu juga sistem operasinya.

Ketika proses / utas yang sedang di-debug menyentuh instruksi trap, yang memicu proses yang dijelaskan oleh @Ben, yang mencakup setengah dari pertukaran konteks yang menangguhkan utas yang saat ini berjalan (yang termasuk menyimpan status CPU-nya ke memori) untuk kemungkinan dimulainya kembali nanti. Karena jebakan ini adalah jebakan breakpoint, sistem operasi menjaga proses debug ditangguhkan menggunakan mungkin mekanisme yang dijelaskan oleh @Ben, dan memberi tahu dan akhirnya melanjutkan debugger.

Debugger menggunakan panggilan sistem, kemudian, untuk mengakses keadaan tersimpan dari proses / utas ditangguhkan.

Untuk mengeksekusi (melanjutkan) baris kode yang rusak (yang sekarang memiliki instruksi jebakan khusus), debugger akan mengembalikan nilai kode mesin asli yang ditimpa dengan instruksi jebakan breakpoint, mungkin mengatur jebakan lain di tempat lain (misalnya jika satu langkah, atau pengguna membuat breakpoint baru), dan tandai proses / utas sebagai runnable, mungkin menggunakan mekanisme seperti yang dijelaskan @Ben.

Detail aktual dapat lebih rumit, karena mempertahankan breakpoint yang berjalan lama yang terkena berarti melakukan sesuatu seperti menukar breakpoint trap untuk kode nyata sehingga baris dapat berjalan, dan kemudian menukar breakpoint kembali ...

Bukankah register yang digunakan oleh proses OS lain terus-menerus? bagaimana mereka tidak ditimpa?

Seperti yang dijelaskan oleh @Ben, menggunakan fitur suspend / resume thread yang sudah ada (konteks switching / swapping multitasking ) yang memungkinkan prosesor untuk dibagikan oleh beberapa proses / utas menggunakan slicing waktu.

Apakah ini hanya snapshot dari konten dan bukan data langsung?

Keduanya. Karena thread yang melanda breakpoint ditangguhkan, snapshot dari data hidup (register CPU, dll ..) pada saat suspensi, dan yang menguasai otoritatif nilai-nilai register CPU untuk mengembalikan ke dalam prosesor seharusnya be benang dilanjutkan . Jika Anda menggunakan antarmuka pengguna debugger untuk membaca dan / atau mengubah register CPU (dari proses yang sedang di-debug) itu akan membaca dan / atau mengubah snapshot / master ini menggunakan panggilan sistem.

Erik Eidt
sumber
1
Yah, sebagian besar arsitektur prosesor mendukung perangkap debug yang misalnya memicu ketika IP (penunjuk instruksi) sama dengan alamat yang disimpan dalam register breakpoint, menghemat kebutuhan untuk menulis ulang kode. (Dengan mencocokkan register selain IP, Anda bisa mendapatkan breakpoint data, dan dengan menjebak setelah setiap instruksi, Anda bisa mendapatkan satu loncatan) Apa yang Anda uraikan tentu saja mungkin, asalkan kode tersebut tidak ada dalam memori yang bisa dibaca.
Ben Voigt
Re "Jika Anda mengubah register CPU ..." pada paragraf terakhir, saya pikir maksud Anda adalah "Jika Anda mengubah salinan register CUP yang disimpan ..." Kemudian ketika OS melanjutkan prosesnya, data yang diubah itu ditulis kembali ke register aktual.
jamesqf
@ jamesqf, ya, terima kasih!
Erik Eidt
@ BenVoigt, setuju. meskipun sementara debugger dapat menangani breakpoint dalam jumlah tidak terbatas, perangkat keras dapat menangani nol atau beberapa, sehingga debugger harus melakukan beberapa juggling.
Erik Eidt
@ jamesqf: Menjelaskan bahwa sebagai salinan agak menyesatkan. Ini penyimpanan resmi untuk status utas sementara utas tidak berjalan.
Ben Voigt
5

Sebenarnya, setidaknya dalam kebanyakan kasus, gdb sendiri tidak menghentikan eksekusi. Sebaliknya, gdb meminta OS, dan OS menghentikan eksekusi.

Awalnya mungkin tampak seperti perbedaan tanpa perbedaan - tapi jujur, memang ada perbedaan. Perbedaannya adalah ini: kemampuan itu sudah dibangun ke dalam OS yang khas, karena itu harus dapat menghentikan sementara dan memulai kembali pelaksanaan utas - ketika utas tidak dijadwalkan untuk berjalan (misalnya, diperlukan beberapa sumber daya yang saat ini tidak tersedia) OS harus menghentikannya sampai dijadwalkan untuk berjalan.

Untuk melakukan itu, OS biasanya memiliki blok memori yang disisihkan untuk setiap utas untuk menyimpan kondisi mesin saat ini. Ketika perlu menjeda utas, kondisi mesin saat ini disimpan di area itu. Ketika perlu melanjutkan utas, status mesin dipulihkan dari area itu.

Ketika debugger perlu menjeda utas, ia memiliki OS menjeda utas yang persis sama dengan alasan lain. Kemudian, untuk membaca status utas yang dijeda, debugger akan melihat status utas yang disimpan. Jika Anda memodifikasi status, debugger akan menulis ke status tersimpan, saat kemudian berlaku saat utas dilanjutkan.

Jerry Coffin
sumber