Saya bekerja di Linux dengan kompiler GCC. Ketika program C ++ saya crash saya ingin secara otomatis menghasilkan stacktrace.
Program saya sedang dijalankan oleh banyak pengguna yang berbeda dan juga berjalan di Linux, Windows dan Macintosh (semua versi dikompilasi menggunakan gcc
).
Saya ingin program saya dapat menghasilkan jejak stack ketika crash dan saat berikutnya pengguna menjalankannya, ia akan bertanya kepada mereka apakah boleh mengirimkan jejak stack kepada saya sehingga saya dapat melacak masalahnya. Saya dapat menangani pengiriman info kepada saya, tetapi saya tidak tahu cara membuat string jejak. Ada ide?
Jawaban:
Untuk Linux dan saya percaya Mac OS X, jika Anda menggunakan gcc, atau kompiler apa pun yang menggunakan glibc, Anda dapat menggunakan fungsi backtrace ()
execinfo.h
untuk mencetak stacktrace dan keluar dengan anggun ketika Anda mendapatkan kesalahan segmentasi. Dokumentasi dapat ditemukan di manual libc .Berikut adalah contoh program yang menginstal
SIGSEGV
handler dan mencetak stacktracestderr
ketika segfaults. Thebaz()
Fungsi sini menyebabkan segfault yang memicu handler:Mengkompilasi dengan
-g -rdynamic
memberi Anda info simbol di output Anda, yang dapat digunakan glibc untuk membuat stacktrace yang bagus:Menjalankan ini membuat Anda mendapatkan hasil ini:
Ini menunjukkan modul muat, offset, dan fungsi yang berasal dari setiap frame dalam tumpukan. Di sini Anda dapat melihat penangan sinyal di atas tumpukan, dan fungsi libc sebelum
main
selainmain
,foo
,bar
, danbaz
.sumber
sigaction()
dalam libc. Walaupun backtrace Anda tampaknya benar, saya terkadang menemukan bahwa langkah-langkah tambahan diperlukan untuk memastikan lokasi kesalahan yang sebenarnya muncul di backtrace karena dapat ditimpasigaction()
oleh kernel.catchsegv
bukan apa yang dibutuhkan OP tetapi luar biasa untuk menangkap kesalahan segmentasi dan mendapatkan semua informasi.Ini bahkan lebih mudah daripada "man backtrace", ada perpustakaan kecil yang didokumentasikan (spesifik GNU) didistribusikan dengan glibc sebagai libSegFault.so, yang saya percaya ditulis oleh Ulrich Drepper untuk mendukung program catchsegv (lihat "man catchsegv").
Ini memberi kita 3 kemungkinan. Alih-alih menjalankan "program -o hai":
Jalankan dalam catchsegv:
Tautan dengan libSegFault saat runtime:
Tautan dengan libSegFault pada waktu kompilasi:
Dalam semua 3 kasus, Anda akan mendapatkan jejak balik yang lebih jelas dengan sedikit optimasi (gcc -O0 atau -O1) dan simbol debugging (gcc -g). Jika tidak, Anda mungkin berakhir dengan tumpukan alamat memori.
Anda juga dapat menangkap lebih banyak sinyal untuk jejak tumpukan dengan sesuatu seperti:
Outputnya akan terlihat seperti ini (perhatikan backtrace di bagian bawah):
Jika Anda ingin mengetahui detail darahnya, sayangnya sumber terbaik adalah sumbernya: Lihat http://sourceware.org/git/?p=glibc.git;a=blob;f=debug/segfault.c dan direktori induknya http://sourceware.org/git/?p=glibc.git;a=tree;f=debug
sumber
-Wl,--no-as-needed
ke flag kompiler. Jika tidak,ld
memang tidak akan terhubunglibSegFault
, karena ia mengenali bahwa biner tidak menggunakan simbolnya.Linux
Sementara penggunaan backtrace () berfungsi di execinfo.h untuk mencetak stacktrace dan keluar dengan anggun ketika Anda mendapatkan kesalahan segmentasi telah disarankan , saya tidak melihat menyebutkan seluk-beluk yang diperlukan untuk memastikan backtrace yang dihasilkan menunjuk ke lokasi aktual dari kesalahan (setidaknya untuk beberapa arsitektur - x86 & ARM).
Dua entri pertama dalam rantai bingkai tumpukan saat Anda masuk ke penangan sinyal berisi alamat kembali di dalam penangan sinyal dan satu di dalam sigaction () di libc. Bingkai tumpukan fungsi terakhir yang dipanggil sebelum sinyal (yang merupakan lokasi kesalahan) hilang.
Kode
Keluaran
Semua bahaya memanggil fungsi backtrace () dalam pengatur sinyal masih ada dan tidak boleh diabaikan, tapi saya menemukan fungsionalitas yang saya jelaskan di sini cukup membantu dalam debugging crash.
Penting untuk dicatat bahwa contoh yang saya berikan dikembangkan / diuji di Linux untuk x86. Saya juga berhasil menerapkan ini pada ARM menggunakan
uc_mcontext.arm_pc
bukanuc_mcontext.eip
.Berikut tautan ke artikel tempat saya mempelajari detail untuk implementasi ini: http://www.linuxjournal.com/article/6391
sumber
-rdynamic
untuk memerintahkan linker untuk menambahkan semua simbol, tidak hanya yang digunakan, ke tabel simbol dinamis. Ini memungkinkanbacktrace_symbols()
untuk mengonversi alamat menjadi nama fungsiaddr2line
perintah entah bagaimana untuk mendapatkan garis yang tepat di mana crash itu terjadi?glibc
uc_mcontext
, tidak berisi bidang bernamaeip
. Sekarang ada array yang perlu diindeks,uc_mcontext.gregs[REG_EIP]
adalah setara.Meskipun jawaban yang benar telah disediakan yang menjelaskan bagaimana cara menggunakan
backtrace()
fungsi libc GNU 1 dan saya memberikan jawaban saya sendiri yang menjelaskan bagaimana memastikan backtrace dari pengarah sinyal menunjuk ke lokasi aktual kesalahan 2 , saya tidak melihat penyebutan demangling C ++ simbol keluaran dari backtrace.Ketika mendapatkan backtraces dari program C ++, output dapat dijalankan melalui
c++filt
1 untuk menghilangkan simbol-simbol atau dengan menggunakan 1 secara langsung.abi::__cxa_demangle
c++filt
dan__cxa_demangle
spesifik untuk GCCContoh C ++ Linux berikut menggunakan penangan sinyal yang sama dengan jawaban saya yang lain dan menunjukkan bagaimana
c++filt
dapat digunakan untuk menghilangkan simbol.Kode :
Output (
./test
):Keluaran Demangled (
./test 2>&1 | c++filt
):Berikut ini dibangun pada penangan sinyal dari jawaban asli saya dan dapat menggantikan penangan sinyal dalam contoh di atas untuk menunjukkan bagaimana
abi::__cxa_demangle
dapat digunakan untuk demangle simbol. Penangan sinyal ini menghasilkan output demangled yang sama seperti contoh di atas.Kode :
sumber
std::cerr
,free()
danexit()
semua melanggar pembatasan terhadap pemanggilan panggilan non-async-signal-safe pada sistem POSIX. Kode ini akan kebuntuan jika proses Anda gagal dalam panggilan apapun sepertifree()
,malloc()
new
, ataudetete
.Mungkin layak untuk melihat Google Breakpad , generator lintas tempat kecelakaan dan alat untuk memproses pembuangan.
sumber
Anda tidak menentukan sistem operasi Anda, jadi ini sulit dijawab. Jika Anda menggunakan sistem berdasarkan gnu libc, Anda mungkin dapat menggunakan fungsi libc
backtrace()
.GCC juga memiliki dua bawaan yang dapat membantu Anda, tetapi yang mungkin atau mungkin tidak diterapkan sepenuhnya pada arsitektur Anda, dan itu adalah
__builtin_frame_address
dan__builtin_return_address
. Keduanya menginginkan tingkat integer langsung (dengan segera, maksud saya tidak bisa menjadi variabel). Jika__builtin_frame_address
untuk level tertentu tidak nol, harus aman untuk mengambil alamat pengirim level yang sama.sumber
Terima kasih untuk antusiasme telah menarik perhatian saya ke utilitas addr2line.
Saya telah menulis skrip yang cepat dan kotor untuk memproses output dari jawaban yang disediakan di sini : (terima kasih banyak kepada jschmier!) Menggunakan utilitas addr2line.
Script menerima argumen tunggal: Nama file yang berisi output dari utilitas jschmier.
Keluaran harus mencetak sesuatu seperti berikut untuk setiap tingkat jejak:
Kode:
sumber
ulimit -c <value>
menetapkan batas ukuran file inti pada unix. Secara default, batas ukuran file inti adalah 0. Anda dapat melihatulimit
nilai Andaulimit -a
.juga, jika Anda menjalankan program dari dalam gdb, itu akan menghentikan program Anda pada "pelanggaran segmentasi" (
SIGSEGV
, umumnya ketika Anda mengakses sepotong memori yang tidak Anda alokasikan) atau Anda dapat mengatur breakpoints.ddd dan nemiver adalah ujung depan untuk gdb yang membuatnya bekerja dengan lebih mudah bagi pemula.
sumber
Penting untuk dicatat bahwa setelah Anda membuat file inti, Anda harus menggunakan alat gdb untuk melihatnya. Agar gdb memahami file inti Anda, Anda harus memberi tahu gcc untuk memasukkan biner dengan simbol debugging: untuk melakukan ini, Anda kompilasi dengan flag -g:
Kemudian, Anda dapat mengatur "ulimit -c unlimited" untuk membiarkannya membuang inti, atau jalankan saja program Anda di dalam gdb. Saya lebih suka pendekatan kedua:
Saya harap ini membantu.
sumber
gdb
langsung dari program mogok Anda. Setup handler untuk SIGSEGV, SEGILL, SIGBUS, SIGFPE yang akan memanggil gdb. Detail: stackoverflow.com/questions/3151779/... Keuntungannya adalah Anda mendapatkan backtrace yang indah dan beranotasi seperti dibt full
, juga Anda bisa mendapatkan jejak tumpukan semua utas.Saya sudah melihat masalah ini untuk sementara waktu.
Dan terkubur dalam di Google Performance Tools README
http://code.google.com/p/google-perftools/source/browse/trunk/README
berbicara tentang libunwind
http://www.nongnu.org/libunwind/
Senang mendengar pendapat perpustakaan ini.
Masalahnya dengan -inamik adalah ia dapat meningkatkan ukuran biner secara signifikan dalam beberapa kasus
sumber
Beberapa versi libc berisi fungsi yang berhubungan dengan jejak stack; Anda mungkin dapat menggunakannya:
http://www.gnu.org/software/libc/manual/html_node/Backtraces.html
Saya ingat menggunakan libunwind dahulu kala untuk mendapatkan jejak tumpukan, tetapi mungkin tidak didukung pada platform Anda.
sumber
Anda dapat menggunakan DeathHandler - kelas C ++ kecil yang melakukan segalanya untuk Anda, andal.
sumber
execlp()
untuk melakukan panggilan addr2line ... akan menyenangkan untuk sepenuhnya tetap dalam program sendiri (yang dimungkinkan dengan memasukkan kode addr2line dalam beberapa bentuk)Lupakan tentang mengubah sumber Anda dan lakukan beberapa peretasan dengan fungsi backtrace () atau macroses - ini hanyalah solusi yang buruk.
Sebagai solusi yang berfungsi dengan baik, saya akan menyarankan:
Ini akan mencetak jejak balik yang dapat dibaca yang tepat dari program Anda dengan cara yang dapat dibaca manusia (dengan nama file sumber dan nomor baris). Selain itu, pendekatan ini akan memberi Anda kebebasan untuk mengotomatiskan sistem Anda: memiliki skrip pendek yang memeriksa apakah proses membuat dump inti, dan kemudian mengirim jejak balik melalui email ke pengembang, atau login ini ke beberapa sistem logging.
sumber
adalah variabel sistem, yang akan memungkinkan untuk membuat dump inti setelah aplikasi Anda crash. Dalam hal ini jumlah yang tidak terbatas. Cari file yang disebut core di direktori yang sama. Pastikan Anda mengkompilasi kode Anda dengan informasi debug yang diaktifkan!
salam
sumber
limit coredumpsize unlimited
Melihat:
man 3 backtrace
Dan:
Ini adalah ekstensi GNU.
sumber
Lihat fasilitas Stack Trace di ACE (ADAPTIVE Communication Environment). Ini sudah ditulis untuk mencakup semua platform utama (dan banyak lagi). Pustaka tersebut berlisensi BSD-style sehingga Anda bahkan dapat menyalin / menempelkan kode jika Anda tidak ingin menggunakan ACE.
sumber
Saya dapat membantu dengan versi Linux: fungsi backtrace, backtrace_symbols dan backtrace_symbols_fd dapat digunakan. Lihat halaman manual yang sesuai.
sumber
Sepertinya di salah satu versi c ++ boost terakhir muncul library untuk memberikan apa yang Anda inginkan, mungkin kodenya akan multiplatform. Ini adalah boost :: stacktrace , yang dapat Anda gunakan seperti pada sampel boost :
Di Linux Anda mengkompilasi kode di atas:
Contoh backtrace yang disalin dari dokumentasi boost :
sumber
* nix: Anda dapat mencegat SIGSEGV (biasanya sinyal ini dinaikkan sebelum mogok) dan menyimpan info tersebut menjadi file. (selain file inti yang dapat Anda gunakan untuk debug menggunakan gdb misalnya).
win: Periksa ini dari msdn.
Anda juga dapat melihat kode chrome google untuk melihat bagaimana menangani crash. Ini memiliki mekanisme penanganan pengecualian yang bagus.
sumber
Saya menemukan bahwa solusi @tgamblin tidak lengkap. Itu tidak bisa menangani dengan stackoverflow. Saya pikir karena secara default handler sinyal dipanggil dengan tumpukan yang sama dan SIGSEGV dilemparkan dua kali. Untuk melindungi Anda perlu mendaftarkan tumpukan independen untuk penangan sinyal.
Anda dapat memeriksa ini dengan kode di bawah ini. Secara default pawang gagal. Dengan STACK_OVERFLOW makro yang didefinisikan, tidak apa-apa.
sumber
Raja baru di kota telah tiba https://github.com/bombela/backward-cpp
1 tajuk untuk ditempatkan dalam kode Anda dan 1 pustaka untuk dipasang.
Secara pribadi saya menyebutnya menggunakan fungsi ini
sumber
Saya akan menggunakan kode yang menghasilkan jejak stack untuk memori yang bocor di Visual Leak Detector . Ini hanya bekerja pada Win32.
sumber
Saya telah melihat banyak jawaban di sini melakukan penangan sinyal dan kemudian keluar. Itulah caranya, tapi ingat fakta yang sangat penting: Jika Anda ingin mendapatkan dump inti untuk kesalahan yang dihasilkan, Anda tidak bisa menelepon
exit(status)
. Panggilabort()
saja!sumber
Sebagai solusi khusus Windows, Anda bisa mendapatkan yang setara dengan jejak tumpukan (dengan informasi jauh lebih banyak) menggunakan Pelaporan Kesalahan Windows . Dengan hanya beberapa entri registri, dapat diatur untuk mengumpulkan dump mode pengguna :
Anda dapat mengatur entri registri dari installer Anda, yang memiliki hak istimewa yang diperlukan.
Membuat dump mode-pengguna memiliki keuntungan berikut daripada menghasilkan jejak stack pada klien:
Perhatikan, bahwa WER hanya dapat dipicu oleh crash aplikasi (yaitu sistem mengakhiri proses karena pengecualian tidak tertangani).
MiniDumpWriteDump
dapat dipanggil kapan saja. Ini mungkin membantu jika Anda perlu membuang kondisi saat ini untuk mendiagnosis masalah selain macet.Bacaan wajib, jika Anda ingin mengevaluasi penerapan dump mini:
sumber
Selain jawaban di atas, berikut cara Anda membuat Debian Linux OS menghasilkan core dump
sumber
Jika Anda masih ingin melakukannya sendiri seperti yang saya lakukan, Anda dapat menautkan
bfd
dan menghindari penggunaanaddr2line
seperti yang saya lakukan di sini:https://github.com/gnif/LookingGlass/blob/master/common/src/crash.linux.c
Ini menghasilkan output:
sumber
Di Linux / unix / MacOSX gunakan file inti (Anda dapat mengaktifkannya dengan ulimit atau panggilan sistem yang kompatibel ). Di Windows gunakan pelaporan kesalahan Microsoft (Anda bisa menjadi mitra dan mendapatkan akses ke data kerusakan aplikasi Anda).
sumber
Saya lupa tentang teknologi "apport" GNOME, tetapi saya tidak tahu banyak tentang cara menggunakannya. Ini digunakan untuk menghasilkan stacktraces dan diagnostik lain untuk diproses dan dapat secara otomatis mengajukan bug. Ini pasti layak untuk dicoba.
sumber