Tutorial debugging GDB untuk pemula

Tutorial debugging GDB untuk pemula

Anda mungkin sudah berpengalaman dalam men -debug skrip bash (lihat cara men -debug skrip bash jika Anda belum terbiasa dengan debugging bash), namun bagaimana cara men -debug c atau c c atau c++? Ayo jelajahi.

GDB adalah utilitas debugging Linux yang sudah lama berdiri dan komprehensif, yang akan memakan waktu bertahun-tahun untuk belajar jika Anda ingin mengetahui alatnya dengan baik. Namun, bahkan untuk pemula, alat ini bisa sangat kuat dan bermanfaat ketika datang untuk men -debug C atau C++.

Misalnya, jika Anda seorang insinyur QA dan ingin men -debug program C dan biner yang sedang dikerjakan tim Anda dan crash, Anda dapat menggunakan GDB untuk mendapatkan backtrace (daftar fungsi tumpukan yang disebut - seperti pohon - yang mana akhirnya menyebabkan kecelakaan). Atau, jika Anda adalah pengembang C atau C ++ dan Anda baru saja memasukkan bug ke dalam kode Anda, maka Anda dapat menggunakan GDB untuk men -debug variabel, kode, dan lainnya! Ayo menyelam!

Dalam tutorial ini Anda akan belajar:

  • Cara menginstal dan menggunakan utilitas gdb dari baris perintah dalam bash
  • Bagaimana melakukan debugging GDB dasar menggunakan konsol dan prompt GDB
  • Pelajari lebih lanjut tentang output terperinci yang dihasilkan GDB
Tutorial debugging GDB untuk pemula

Persyaratan dan konvensi perangkat lunak yang digunakan

Persyaratan Perangkat Lunak dan Konvensi Baris Perintah Linux
Kategori Persyaratan, konvensi atau versi perangkat lunak yang digunakan
Sistem Distribusi Linux-independen
Perangkat lunak Baris perintah Bash dan GDB, sistem berbasis Linux
Lainnya Utilitas GDB dapat diinstal menggunakan perintah yang disediakan di bawah ini
Konvensi # - mengharuskan Linux -Commands untuk dieksekusi dengan hak istimewa root baik secara langsung sebagai pengguna root atau dengan menggunakan sudo memerintah
$-mengharuskan Linux-Commands untuk dieksekusi sebagai pengguna biasa

Menyiapkan GDB dan Program Uji

Untuk artikel ini, kita akan melihat yang kecil tes.C Program dalam Bahasa Pengembangan C, yang memperkenalkan kesalahan divisi demi nol dalam kode. Kode ini sedikit lebih lama dari apa yang dibutuhkan dalam kehidupan nyata (beberapa baris akan dilakukan, dan tidak diperlukan penggunaan fungsi), tetapi ini dilakukan dengan sengaja untuk menyoroti bagaimana nama fungsi dapat dilihat dengan jelas di dalam GDB saat debugging.

Pertama -tama mari kita instal alat yang akan kita perlukan Sudo Apt Instal (atau Instalasi sudo yum Jika Anda menggunakan distribusi berbasis topi merah):

sudo apt menginstal gdb build-esential gcc 

Itu build-esensial Dan GCC akan membantu Anda mengkompilasi tes.C C Program di Sistem Anda.

Selanjutnya, mari kita tentukan tes.C skrip sebagai berikut (Anda dapat menyalin dan menempelkan yang berikut ini ke editor favorit Anda dan menyimpan file sebagai tes.C):

int aktual_calc (int a, int b) int c; C = A/B; kembali 0;  int calc () int a; int b; a = 13; b = 0; aktual_calc (a, b); kembali 0;  int main () calc (); kembali 0;  


Beberapa catatan tentang skrip ini: Anda dapat melihatnya saat utama fungsi akan dimulai ( utama Fungsi adalah yang selalu merupakan fungsi utama dan pertama yang dipanggil saat Anda memulai biner yang dikompilasi, ini adalah bagian dari standar C), segera memanggil fungsi calc, yang pada gilirannya menelepon atutual_calc Setelah mengatur beberapa variabel A Dan B ke 13 Dan 0 masing -masing.

Mengeksekusi skrip kami dan mengkonfigurasi dump inti

Mari kita kumpulkan skrip ini menggunakan GCC dan menjalankan hal yang sama:

$ gcc -ggdb tes.C -O tes.keluar $ ./tes.Exception Out Floating Point (Core Dumped) 

Itu -GGDB opsi untuk GCC akan memastikan bahwa sesi debugging kami menggunakan GDB akan menjadi yang ramah; itu menambahkan informasi debugging spesifik GDB ke tes.keluar biner. Kami menyebutkan file biner output ini menggunakan -Hai opsi untuk GCC, Dan sebagai masukan, kami memiliki skrip kami tes.C.

Saat kami menjalankan skrip, kami segera mendapatkan pesan samar Exception Titik Mengambang (Core Dumped). Bagian yang kami minati untuk saat ini adalah Inti dibuang pesan. Jika Anda tidak melihat pesan ini (atau jika Anda melihat pesan tetapi tidak dapat menemukan file inti), Anda dapat mengatur pembuangan inti yang lebih baik sebagai berikut:

jika ! kernel grep -qi '.core_pattern ' /etc /sysctl.conf; lalu sudo sh -c 'echo "kernel.core_pattern = core.%P.%u.%S.%e.%t ">> /etc /sysctl.conf 'sudo sysctl -p fi ulimit -c tidak terbatas 

Di sini kita pertama kali memastikan tidak ada pola inti kernel linux (inti.core_pattern) Pengaturan yang dibuat di /etc/sysctl.conf (File konfigurasi untuk mengatur variabel sistem pada ubuntu dan sistem operasi lainnya), dan - asalkan tidak ada pola inti yang ada - tambahkan pola nama file inti yang praktis (inti.%P.%u.%S.%e.%T) ke file yang sama.

Itu sysctl -p perintah (untuk dieksekusi sebagai root, karenanya sudo) Selanjutnya memastikan file segera dimuat ulang tanpa memerlukan reboot. Untuk informasi lebih lanjut tentang pola inti, Anda dapat melihat Penamaan file pembuangan inti bagian yang dapat diakses dengan menggunakan inti manusia memerintah.

Akhirnya, ULIMIT -C tidak terbatas Perintah cukup mengatur maksimum ukuran file inti ke tak terbatas untuk sesi ini. Pengaturan ini bukan gigih melintasi restart. Untuk membuatnya permanen, Anda dapat melakukannya:

kucing sudo -c " < /etc/security/limits.conf * soft core unlimited * hard core unlimited EOF 

Yang akan menambah * Soft Core Unlimited Dan * Hard Core Unlimited ke /etc/keamanan/batasan.conf, memastikan tidak ada batasan untuk pembuangan inti.

Ketika Anda sekarang mengeksekusi kembali tes.keluar file Anda harus melihat Inti dibuang Pesan dan Anda harus dapat melihat file inti (dengan pola inti yang ditentukan), sebagai berikut:

inti $ ls.1341870.1000.8.tes.keluar.Tes 1598867712.uji C.keluar 

Mari selanjutnya periksa metadata file inti:

$ inti file.1341870.1000.8.tes.keluar.1598867712 Core.1341870.1000.8.tes.keluar.1598867712: File inti LSB 64-bit, x86-64, versi 1 (sysv), gaya Svr4, dari './tes.out ', uid nyata: 1000, uid efektif: 1000, gid nyata: 1000, gid efektif: 1000, execfn:'./tes.out ', platform:' x86_64 ' 

Kita dapat melihat bahwa ini adalah file inti 64-bit, ID pengguna mana yang digunakan, apa platformnya, dan akhirnya apa yang dapat dieksekusi digunakan. Kami juga dapat melihat dari nama file (.8.) bahwa itu adalah sinyal 8 yang mengakhiri program. Sinyal 8 adalah SIGFPE, pengecualian titik mengambang. GDB nantinya akan menunjukkan kepada kita bahwa ini adalah pengecualian aritmatika.

Menggunakan GDB untuk menganalisis dump inti

Mari kita buka file inti dengan GDB dan asumsikan sebentar kita tidak tahu apa yang terjadi (jika Anda seorang pengembang berpengalaman, Anda mungkin sudah melihat bug yang sebenarnya di sumbernya!):

$ GDB ./tes.keluar ./inti.1341870.1000.8.tes.keluar.1598867712 GNU GDB (Ubuntu 9.1-0ubuntu1) 9.1 Hak Cipta (C) 2020 Free Software Foundation, Inc. Lisensi GPLV3+: GNU GPL versi 3 atau lebih baru ini adalah perangkat lunak gratis: Anda bebas untuk mengubah dan mendistribusikannya kembali. Tidak ada jaminan, sejauh diizinkan oleh hukum. Ketik "Tampilkan Salinan" dan "Tampilkan Garansi" untuk detailnya. GDB ini dikonfigurasi sebagai "x86_64-linux-gnu". Ketik "Tampilkan Konfigurasi" untuk detail konfigurasi. Untuk instruksi pelaporan bug, silakan lihat: . Temukan manual GDB dan sumber daya dokumentasi lainnya secara online di: . Untuk bantuan, ketik "bantuan". Ketik "Words Word" untuk mencari perintah yang terkait dengan "kata" ... membaca simbol dari ./tes.keluar ... [LWP baru 1341870] Core dihasilkan oleh './tes.keluar'. Program diakhiri dengan sinyal SIGFPE, pengecualian aritmatika. #0 0x000056468844813b di aktual_calc (a = 13, b = 0) saat tes.C: 3 3 C = A/B; (GDB) 


Seperti yang Anda lihat, pada baris pertama kami menelepon GDB dengan opsi pertama biner kami dan sebagai opsi kedua file inti. Cukup ingat biner dan inti. Selanjutnya kita melihat GDB menginisialisasi, dan kami disajikan dengan beberapa informasi.

Jika Anda melihat a PERINGATAN: Ukuran bagian yang tidak terduga.REG-XSTATE/1341870 'dalam file inti.'Atau pesan serupa, Anda mungkin mengabaikannya untuk saat ini.

Kita melihat bahwa pembuangan inti dihasilkan oleh tes.keluar dan diberitahu bahwa sinyalnya adalah pengecualian aritmatika sigfpe. Besar; kita sudah tahu ada sesuatu yang salah dengan matematika kita, dan mungkin tidak dengan kode kita!

Selanjutnya kita melihat bingkai (tolong pikirkan a bingkai seperti prosedur dalam kode untuk saat ini) di mana program diakhiri: bingkai #0. GDB Menambahkan segala macam informasi praktis ke ini: Alamat memori, nama prosedur aktual_calc, apa nilai variabel kami, dan bahkan pada satu baris (3) dari file mana (tes.C) masalah ini terjadi.

Selanjutnya kita melihat baris kode (baris 3) Sekali lagi, kali ini dengan kode yang sebenarnya (C = A/B;) dari garis itu termasuk. Akhirnya kami disajikan dengan prompt GDB.

Masalahnya mungkin sangat jelas sekarang; kita telah melakukannya C = A/B, atau dengan variabel diisi c = 13/0. Tetapi manusia tidak dapat membelah dengan nol, dan karena itu komputer tidak bisa. Seperti yang tidak ada yang memberi tahu komputer bagaimana membagi dengan nol, pengecualian terjadi, pengecualian aritmatika, pengecualian / kesalahan titik mengambang.

Backtracing

Jadi mari kita lihat apa lagi yang bisa kita temukan tentang GDB. Mari kita lihat beberapa perintah dasar. Tinju adalah yang paling sering Anda gunakan: bt:

(GDB) BT #0 0x000056468844813b di Actual_Calc (A = 13, B = 0) Saat tes.C: 3 #1 0x0000564688448171 Dalam Calc () saat tes.C: 12 #2 0x000056468844818a di Main () Saat tes.C: 17 

Perintah ini adalah steno untuk Backtrace dan pada dasarnya memberi kita jejak keadaan saat ini (Prosedur setelah prosedur dipanggil) dari program tersebut. Pikirkan seperti urutan terbalik hal -hal yang terjadi; bingkai #0 (frame pertama) adalah fungsi terakhir yang sedang dieksekusi oleh program saat macet, dan bingkai #2 adalah bingkai pertama yang dipanggil saat program dimulai.

Dengan demikian kita dapat menganalisis apa yang terjadi: program dimulai, dan utama() secara otomatis dipanggil. Berikutnya, utama() ditelepon calc () (dan kami dapat mengonfirmasi ini dalam kode sumber di atas), dan akhirnya calc () ditelepon aktual_calc Dan ada yang salah.

Baik, kita dapat melihat setiap baris di mana sesuatu terjadi. Misalnya, aktual_calc () Fungsi dipanggil dari baris 12 di tes.C. Perhatikan bahwa itu tidak calc () yang dipanggil dari baris 12 melainkan aktual_calc () yang masuk akal; tes.C akhirnya mengeksekusi ke baris 12 sejauh calc () fungsi menyangkut, karena di sinilah calc () fungsi disebut aktual_calc ().

Tip Pengguna Daya: Jika Anda menggunakan beberapa utas, Anda dapat menggunakan perintah tersebut Thread Terapkan Semua BT untuk mendapatkan backtrace untuk semua utas yang berjalan saat program macet!

Inspeksi bingkai

Jika kita mau, kita dapat memeriksa setiap bingkai, kode sumber yang cocok (jika tersedia), dan setiap variabel langkah demi langkah:

(GDB) F 2 #2 0x000055FA2323318A Dalam Main () saat tes.C: 17 17 Calc (); (GDB) Daftar 12 aktual_calc (a, b); 13 kembali 0; 14 15 16 int main () 17 calc (); 18 kembali 0; 19 (GDB) P A no Symbol "a" dalam konteks saat ini. 

Di sini kita 'melompat ke' bingkai 2 dengan menggunakan f 2 memerintah. F adalah tangan yang pendek untuk bingkai memerintah. Selanjutnya kami mencantumkan kode sumber dengan menggunakan daftar perintah, dan akhirnya mencoba mencetak (menggunakan P perintah steno) nilai dari A variabel, yang gagal, seperti pada titik ini A belum didefinisikan pada titik ini dalam kode; Perhatikan kami bekerja di baris 17 di fungsi utama(), dan konteks aktual yang ada di dalam batas fungsi/bingkai ini.

Perhatikan bahwa fungsi tampilan kode sumber, termasuk beberapa kode sumber yang ditampilkan di output sebelumnya di atas, hanya tersedia jika kode sumber aktual tersedia.

Di sini kami juga segera melihat gotcha; Jika kode sumber berbeda maka kode yang dikompilasi oleh biner, seseorang dapat dengan mudah disesatkan; Output dapat menunjukkan sumber yang tidak dapat diterapkan / diubah. GDB melakukannya bukan Periksa apakah ada kecocokan revisi kode sumber! Dengan demikian sangat penting bahwa Anda menggunakan revisi kode sumber yang sama persis dengan yang dari mana biner Anda dikompilasi.

Alternatifnya adalah tidak menggunakan kode sumber sama sekali, dan cukup debug situasi tertentu dalam fungsi tertentu, menggunakan revisi kode sumber yang lebih baru. Ini sering terjadi untuk pengembang dan debugger tingkat lanjut yang kemungkinan tidak membutuhkan terlalu banyak petunjuk tentang di mana masalah tersebut mungkin dalam fungsi tertentu dan dengan nilai variabel yang disediakan.

Mari kita periksa bingkai 1:

(GDB) F 1 #1 0x000055FA23233171 Dalam Calc () saat tes.C: 12 12 aktual_calc (a, b); (gdb) daftar 7 int calc () 8 int a; 9 int b; 10 a = 13; 11 b = 0; 12 aktual_calc (a, b); 13 kembali 0; 14 15 16 int main ()  

Di sini kita dapat lagi melihat banyak informasi yang dihasilkan oleh GDB yang akan membantu pengembang dalam men -debug masalah yang ada. Karena kita sekarang masuk calc (pada baris 12), dan kami telah menginisialisasi dan selanjutnya mengatur variabel A Dan B ke 13 Dan 0 masing -masing, kita sekarang dapat mencetak nilainya:

(GDB) P A $ 1 = 13 (GDB) P B $ 2 = 0 (GDB) P C Tidak ada simbol "c" dalam konteks saat ini. (GDB) divisi p a/b oleh nol 


Perhatikan bahwa saat kami mencoba dan mencetak nilainya C, Masih gagal seperti lagi C belum didefinisikan hingga titik ini (pengembang dapat berbicara tentang 'dalam konteks ini').

Akhirnya, kami melihat ke dalam bingkai #0, Bingkai Crashing kami:

(GDB) F 0 #0 0x000055FA2323313B Di aktual_calc (a = 13, b = 0) saat tes.C: 3 3 C = A/B; (GDB) P A $ 3 = 13 (GDB) P B $ 4 = 0 (GDB) P C $ 5 = 22010 

Semua jelas, kecuali nilai yang dilaporkan C. Perhatikan bahwa kami telah mendefinisikan variabel C, tetapi belum memberinya nilai awal. Dengan demikian C benar -benar tidak terdefinisi (dan itu tidak diisi oleh persamaan C = A/B Namun karena itu gagal) dan nilai yang dihasilkan kemungkinan dibaca dari beberapa ruang alamat yang variabelnya C ditugaskan (dan ruang memori itu belum diinisialisasi/dibersihkan).

Kesimpulan

Besar. Kami dapat men -debug dump inti untuk program C, dan kami bersandar pada dasar -dasar debugging GDB sementara itu. Jika Anda seorang insinyur QA, atau pengembang junior, dan Anda telah memahami dan mempelajari semuanya dalam tutorial ini dengan baik, Anda sudah sedikit di depan sebagian besar insinyur QA, dan berpotensi pengembang lain di sekitar Anda.

Dan lain kali Anda menonton Star Trek dan Kapten Janeway atau Kapten Picard ingin 'membuang inti', Anda pasti akan membuat senyum yang lebih luas. Nikmati debugging inti Anda berikutnya, dan tinggalkan komentar di bawah ini dengan petualangan debugging Anda.

Tutorial Linux Terkait:

  • Pengantar Otomatisasi Linux, Alat dan Teknik
  • Hal -hal yang harus diinstal pada ubuntu 20.04
  • Menguasai loop skrip bash
  • Hal -hal yang harus dilakukan setelah menginstal ubuntu 20.04 FOSSA FOSSA Linux
  • Mint 20: Lebih baik dari Ubuntu dan Microsoft Windows?
  • Ubuntu 20.04 Panduan
  • Hal -hal yang harus diinstal pada Ubuntu 22.04
  • Loop bersarang dalam skrip bash
  • Cara Dual Boot Kali Linux dan Windows 10
  • Sistem Linux Hung? Cara melarikan diri ke baris perintah dan…