Tutorial Debugging GDB untuk Pemula

Tutorial Debugging GDB untuk Pemula

Anda mungkin sudah mahir dalam menyahpepijat skrip bash (lihat cara debug skrip bash jika anda tidak biasa dengan debugging bash lagi), namun bagaimana debug c atau c++? Mari kita meneroka.

GDB adalah utiliti debugging linux yang lama dan komprehensif, yang akan mengambil masa bertahun-tahun untuk mengetahui jika anda ingin mengetahui alat itu dengan baik. Walau bagaimanapun, walaupun untuk pemula, alat ini boleh menjadi sangat kuat dan berguna ketika debugging c atau c++.

Sebagai contoh, jika anda seorang jurutera QA dan ingin menyahpepijat program C dan binari pasukan anda sedang bekerja dan ia terhempas, anda boleh menggunakan GDB untuk mendapatkan backtrace (senarai timbunan fungsi yang dipanggil - seperti pokok - yang mana akhirnya menyebabkan kemalangan itu). Atau, jika anda adalah pemaju C atau C ++ dan anda hanya memperkenalkan bug ke dalam kod anda, maka anda boleh menggunakan GDB untuk debug pembolehubah, kod dan banyak lagi! Mari menyelam masuk!

Dalam tutorial ini anda akan belajar:

  • Cara Memasang dan Menggunakan Utiliti GDB Dari Talian Perintah di Bash
  • Cara Membuat Debugging GDB Asas Menggunakan Konsol GDB dan Prompt
  • Ketahui lebih lanjut mengenai output terperinci GDB menghasilkan
Tutorial Debugging GDB untuk Pemula

Keperluan perisian dan konvensyen yang digunakan

Keperluan Perisian dan Konvensyen Talian Perintah Linux
Kategori Keperluan, konvensyen atau versi perisian yang digunakan
Sistem Pengedaran linux-bebas
Perisian Baris perintah bash dan gdb, sistem berasaskan linux
Yang lain Utiliti GDB boleh dipasang menggunakan arahan yang disediakan di bawah
Konvensyen # - memerlukan komando linux untuk dilaksanakan dengan keistimewaan akar sama ada secara langsung sebagai pengguna root atau dengan menggunakan sudo perintah
$-Memerlukan komando linux untuk dilaksanakan sebagai pengguna yang tidak istimewa

Menyediakan GDB dan program ujian

Untuk artikel ini, kita akan melihat yang kecil ujian.c program dalam bahasa pembangunan c, yang memperkenalkan ralat divisi demi-sifar dalam kod. Kod ini sedikit lebih lama maka apa yang diperlukan dalam kehidupan sebenar (beberapa baris akan dilakukan, dan tiada penggunaan fungsi diperlukan), tetapi ini dilakukan dengan tujuan untuk menyerlahkan bagaimana nama fungsi dapat dilihat dengan jelas di dalam GDB ketika debugging.

Mari kita pasang alat terlebih dahulu yang kita perlukan menggunakan Sudo apt pemasangan (atau Pemasangan sudo yum Jika anda menggunakan pengedaran berasaskan topi merah):

sudo apt pasang gcc binaan gdb 

The Build-Essential dan GCC akan membantu anda menyusun ujian.c Program c pada sistem anda.

Seterusnya, marilah kita menentukan ujian.c skrip seperti berikut (anda boleh menyalin dan tampal berikut ke editor kegemaran anda dan simpan fail sebagai ujian.c):

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


Beberapa nota mengenai skrip ini: anda dapat melihatnya ketika utama fungsi akan dimulakan (The utama fungsi adalah fungsi utama dan pertama yang dipanggil apabila anda memulakan binari yang disusun, ini adalah sebahagian daripada standard c), ia segera memanggil fungsi tersebut calc, Yang seterusnya memanggil atual_calc Setelah menetapkan beberapa pembolehubah a dan b ke 13 dan 0 masing -masing.

Melaksanakan skrip kami dan mengkonfigurasi sampah teras

Marilah kita menyusun skrip ini menggunakan GCC dan melaksanakan yang sama:

$ gcc -ggdb ujian.c -o ujian.keluar $ ./ujian.Pengecualian titik terapung (teras dibuang) 

The -GGDB pilihan untuk GCC akan memastikan bahawa sesi debug kami menggunakan GDB akan menjadi yang mesra; ia menambah maklumat penyahpepijatan spesifik GDB ke ujian.keluar binari. Kami namakan fail binari output ini menggunakan -o pilihan untuk GCC, Dan sebagai input kita mempunyai skrip kita ujian.c.

Semasa kami melaksanakan skrip, kami segera mendapat mesej misteri Pengecualian titik terapung (teras dibuang). Bahagian yang kita berminat buat masa ini adalah teras dibuang mesej. Jika anda tidak melihat mesej ini (atau jika anda melihat mesej tetapi tidak dapat mencari fail teras), anda boleh menyiapkan pembuangan teras yang lebih baik seperti berikut:

jika ! kernel grep -qi '.core_pattern ' /etc /sysctl.Con; Kemudian sudo sh -c 'echo "kernel.CORE_PATTERN = CORE.%ms.%u.%s.%e.%t ">> /etc /sysctl.conf 'sudo sysctl -p fi ulimit -c tanpa had 

Di sini kita mula -mula memastikan tiada corak teras kernel Linux (kernel.core_pattern) tetapan dibuat lagi /etc/sysctl.Conf (Fail konfigurasi untuk menetapkan pembolehubah sistem di Ubuntu dan sistem operasi lain), dan - tidak menyediakan corak teras sedia ada - tambahkan corak nama fail teras yang berguna (teras.%ms.%u.%s.%e.%t) ke fail yang sama.

The SYSCTL -P perintah (akan dilaksanakan sebagai akar, oleh itu sudo) seterusnya memastikan fail segera dimuat semula tanpa memerlukan reboot. Untuk maklumat lanjut mengenai corak teras, anda dapat melihat Penamaan fail dump teras bahagian yang boleh diakses dengan menggunakan Lelaki teras perintah.

Akhirnya, ULIMIT -C Unlimited perintah hanya menetapkan saiz fail teras maksimum ke tidak terhad untuk sesi ini. Tetapan ini adalah tidak berterusan merentasi restart. Untuk menjadikannya kekal, anda boleh lakukan:

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

Yang akan ditambah * Core Soft Unlimited dan * Hard Core Unlimited ke /etc/keselamatan/had.Conf, memastikan tiada had untuk membuang teras.

Apabila anda sekarang melaksanakan semula ujian.keluar fail anda mesti melihat teras dibuang mesej dan anda sepatutnya dapat melihat fail teras (dengan corak teras yang ditentukan), seperti berikut:

$ ls teras.1341870.1000.8.ujian.keluar.1598867712 Ujian.c ujian.keluar 

Mari kita periksa metadata fail teras:

$ fail teras.1341870.1000.8.ujian.keluar.1598867712 teras.1341870.1000.8.ujian.keluar.1598867712: Fail teras LSB 64-bit, x86-64, versi 1 (SYSV), gaya SVR4, dari './ujian.keluar ', uid sebenar: 1000, efektif uid: 1000, GID sebenar: 1000, GID berkesan: 1000, execfn:'./ujian.keluar ', platform:' x86_64 ' 

Kita dapat melihat bahawa ini adalah fail teras 64-bit, yang mana ID pengguna digunakan, platform apa itu, dan akhirnya apa yang boleh dilaksanakan digunakan. Kita juga dapat melihat dari nama fail (.8.) bahawa ia adalah isyarat 8 yang menamatkan program. Isyarat 8 adalah sigfpe, pengecualian titik terapung. GDB kemudian akan menunjukkan kepada kita bahawa ini adalah pengecualian aritmetik.

Menggunakan GDB untuk menganalisis pembuangan teras

Mari buka fail teras dengan GDB dan anggap sebentar kita tidak tahu apa yang berlaku (jika anda seorang pemaju berpengalaman, anda mungkin telah melihat bug sebenar di sumbernya!):

$ gdb ./ujian.keluar ./teras.1341870.1000.8.ujian.keluar.1598867712 GNU GDB (Ubuntu 9.1-0ubuntu1) 9.1 Hak Cipta (c) 2020 Yayasan Perisian Percuma, Inc. Lesen GPLV3+: GNU GPL Versi 3 atau lebih baru ini adalah perisian percuma: anda bebas menukar dan mengagihkan semula. Tidak ada jaminan, setakat yang dibenarkan oleh undang -undang. Taip "Tunjukkan Penyalinan" dan "Tunjukkan Waranti" untuk maklumat lanjut. GDB ini dikonfigurasikan sebagai "x86_64-linux-gnu". Taipkan "Tunjukkan Konfigurasi" untuk perincian konfigurasi. Untuk arahan pelaporan pepijat, sila lihat: . Cari manual GDB dan sumber dokumentasi lain dalam talian di: . Untuk bantuan, taipkan "Bantuan". Taipkan "perkataan apropos" untuk mencari arahan yang berkaitan dengan "perkataan" ... simbol membaca dari ./ujian.keluar ... [LWP baru 1341870] Teras dihasilkan oleh './ujian.keluar'. Program yang ditamatkan dengan isyarat sigfpe, pengecualian aritmetik. #0 0x00000056468844813b di actual_calc (a = 13, b = 0) di ujian.C: 3 3 C = A/B; (GDB) 


Seperti yang anda lihat, pada baris pertama yang kami panggil GDB dengan pilihan pertama kami binari dan pilihan kedua fail teras. Hanya ingat binari dan teras. Seterusnya kita melihat GDB memulakan, dan kami dibentangkan dengan beberapa maklumat.

Sekiranya anda melihat a Amaran: Saiz bahagian yang tidak dijangka.Reg-xstate/1341870 'dalam fail teras.'Atau mesej yang serupa, anda mungkin mengabaikannya buat masa ini.

Kita melihat bahawa pembuangan teras dihasilkan oleh ujian.keluar dan diberitahu bahawa isyarat itu adalah pengecualian aritmetik,. Hebat; Kita sudah tahu ada sesuatu yang tidak kena dengan matematik kita, dan mungkin tidak dengan kod kita!

Seterusnya kita melihat bingkai (sila fikirkan tentang bingkai seperti yang prosedur dalam kod buat masa ini) di mana program ditamatkan: bingkai #0. GDB menambah pelbagai maklumat berguna untuk ini: alamat memori, nama prosedur actual_calc, Apa nilai pembolehubah kita, dan juga pada satu baris (3) dari fail mana (ujian.c) masalah itu berlaku.

Seterusnya kita melihat garis kod (baris 3) Sekali lagi, kali ini dengan kod sebenar (C = A/B;) dari garis itu termasuk. Akhirnya kami dibentangkan dengan arahan GDB.

Isu ini mungkin sangat jelas sekarang; Kami telah lakukan c = a/b, atau dengan pembolehubah diisi c = 13/0. Tetapi manusia tidak dapat membahagikan sifar, dan komputer tidak dapat sama ada. Ketika tidak ada yang memberitahu komputer bagaimana untuk membahagikan sifar, pengecualian berlaku, pengecualian aritmetik, pengecualian / kesilapan titik terapung.

Backtracing

Oleh itu, mari kita lihat apa lagi yang dapat kita temukan mengenai GDB. Mari lihat beberapa arahan asas. Penumbuk adalah yang paling mungkin anda gunakan paling kerap: Bt:

(gdb) bt #0 0x000056468844813b di actual_calc (a = 13, b = 0) pada ujian.C: 3 #1 0x0000564688448171 dalam calc () di ujian.C: 12 #2 0x000056468844818a di Utama () di Ujian.C: 17 

Perintah ini adalah singkat untuk backtrace Dan pada dasarnya memberi kita jejak keadaan semasa (prosedur selepas prosedur yang dipanggil) program. Fikirkanlah seperti susunan perkara yang berlaku; bingkai #0 (bingkai pertama) adalah fungsi terakhir yang sedang dilaksanakan oleh program ketika ia terhempas, dan bingkai #2 adalah bingkai pertama yang dipanggil ketika program dimulakan.

Oleh itu, kita dapat menganalisis apa yang berlaku: program bermula, dan utama () dipanggil secara automatik. Seterusnya, utama () dipanggil calc () (Dan kita dapat mengesahkannya dalam kod sumber di atas), dan akhirnya calc () dipanggil actual_calc Dan ada perkara yang salah.

Baiklah, kita dapat melihat setiap baris di mana sesuatu berlaku. Contohnya, actual_calc () fungsi dipanggil dari baris 12 di ujian.c. Perhatikan bahawa tidak calc () yang dipanggil dari baris 12 melainkan actual_calc () yang masuk akal; ujian.c akhirnya melaksanakan ke baris 12 sejauh calc () fungsi berkenaan, kerana ini adalah di mana calc () fungsi yang dipanggil actual_calc ().

Petua Pengguna Kuasa: Sekiranya anda menggunakan pelbagai benang, anda boleh menggunakan arahan Thread Memohon Semua BT untuk mendapatkan backtrace untuk semua benang yang berjalan ketika program terhempas!

Pemeriksaan bingkai

Sekiranya kita mahu, kita boleh memeriksa setiap bingkai, kod sumber yang sepadan (jika tersedia), dan setiap langkah pembolehubah demi langkah:

(GDB) F 2 #2 0x000055FA2323318A di Utama () di Ujian.C: 17 17 Calc (); (GDB) Senarai 12 ACUAL_CALC (A, B); 13 kembali 0; 14 15 16 int main () 17 calc (); 18 kembali 0; 19 (gdb) p a simbol "a" dalam konteks semasa. 

Di sini kita 'melompat ke' bingkai 2 dengan menggunakan F 2 perintah. f adalah tangan pendek untuk bingkai perintah. Seterusnya kami menyenaraikan kod sumber dengan menggunakan senarai perintah, dan akhirnya cuba mencetak (menggunakan p perintah shorthand) nilai a pemboleh ubah, yang gagal, pada ketika ini a belum ditakrifkan pada masa ini dalam kod; Perhatikan kami bekerja di baris 17 dalam fungsi utama (), dan konteks sebenar yang ada di dalam batas fungsi/bingkai ini.

Perhatikan bahawa fungsi paparan kod sumber, termasuk beberapa kod sumber yang dipaparkan dalam output sebelumnya di atas, hanya tersedia jika kod sumber sebenar tersedia.

Di sini kita juga melihat gotcha; Sekiranya kod sumbernya berbeza maka kod yang telah disusun dari binari, seseorang dapat dengan mudah disesatkan; Output mungkin menunjukkan sumber yang tidak boleh digunakan / berubah. GDB tidak tidak periksa sama ada terdapat perlawanan semakan kod sumber! Oleh itu, adalah sangat penting bahawa anda menggunakan semakan kod sumber yang sama seperti yang mana binari anda disusun.

Alternatifnya adalah untuk tidak menggunakan kod sumber sama sekali, dan hanya debug situasi tertentu dalam fungsi tertentu, menggunakan semakan semula kod sumber yang lebih baru. Ini sering berlaku untuk pemaju lanjutan dan debuggers yang mungkin tidak memerlukan terlalu banyak petunjuk tentang di mana isu itu mungkin dalam fungsi tertentu dan dengan nilai pembolehubah yang disediakan.

Mari kita periksa bingkai 1:

(GDB) F 1 #1 0x000055FA23233171 dalam Calc () di ujian.c: 12 12 actual_calc (a, b); (gdb) senarai 7 int calc () 8 int a; 9 int b; 10 a = 13; 11 b = 0; 12 actual_calc (a, b); 13 kembali 0; 14 15 16 int main ()  

Di sini kita dapat sekali lagi melihat banyak maklumat yang dikeluarkan oleh GDB yang akan membantu pemaju dalam menyahpepijat masalah di tangan. Oleh kerana kita sekarang calc (pada baris 12), dan kami telah memulakan dan seterusnya menetapkan pembolehubah a dan b ke 13 dan 0 masing -masing, kita kini boleh mencetak nilai mereka:

(GDB) P A $ 1 = 13 (GDB) P B $ 2 = 0 (GDB) P C NO Simbol "C" dalam konteks semasa. (GDB) P A/B Bahagian dengan sifar 


Perhatikan bahawa semasa kita mencuba dan mencetak nilai c, ia masih gagal lagi c belum ditakrifkan sehingga ke tahap ini (pemaju boleh bercakap tentang 'dalam konteks ini') lagi.

Akhirnya, kita melihat bingkai #0, bingkai yang terhempas:

(GDB) F 0 #0 0x0000555FA2323313B di actual_calc (a = 13, b = 0) pada ujian.C: 3 3 C = A/B; (GDB) P A $ 3 = 13 (GDB) P B $ 4 = 0 (GDB) P C $ 5 = 22010 

Semua jelas diri, kecuali nilai yang dilaporkan untuk c. Perhatikan bahawa kami telah menentukan pembolehubah c, tetapi belum memberikan nilai awal. Seperti c benar -benar tidak jelas (dan ia tidak diisi dengan persamaan c = a/b namun seperti yang gagal) dan nilai yang terhasil mungkin dibaca dari beberapa ruang alamat yang mana pembolehubah c ditugaskan (dan ruang ingatan belum dimulakan/dibersihkan lagi).

Kesimpulan

Hebat. Kami dapat menyahpepijat dump teras untuk program C, dan kami menyandarkan asas -asas debug GDB dalam masa yang sama. Jika anda seorang jurutera QA, atau pemaju junior, dan anda telah memahami dan mempelajari segala -galanya dalam tutorial ini dengan baik, anda sudah cukup sedikit di hadapan kebanyakan jurutera QA, dan pemaju lain yang berpotensi di sekeliling anda.

Dan pada masa akan datang anda menonton Star Trek dan Kapten Janeway atau Kapten Picard ingin 'membuang teras', anda pasti akan tersenyum lebih luas. Nikmati Debugging Teras Dibuang Seterusnya, dan tinggalkan kami komen di bawah dengan pengembaraan debug anda.

Tutorial Linux Berkaitan:

  • Pengenalan kepada Automasi, Alat dan Teknik Linux
  • Perkara yang hendak dipasang di Ubuntu 20.04
  • Menguasai Gelung Skrip Bash
  • Perkara yang perlu dilakukan setelah memasang ubuntu 20.04 Focal Fossa Linux
  • Mint 20: Lebih baik daripada Ubuntu dan Microsoft Windows?
  • Ubuntu 20.04 Panduan
  • Perkara yang perlu dipasang di Ubuntu 22.04
  • Gelung bersarang dalam skrip bash
  • Cara Dual Boot Kali Linux dan Windows 10
  • Sistem Hung Linux? Cara melarikan diri ke baris arahan dan ..