Saya mendapat jenis masalah "Schroedinger's Cat" di sini - program saya (sebenarnya rangkaian pengujian untuk program saya, tapi tetap saja sebuah program) macet, tetapi hanya ketika dibangun dalam mode rilis, dan hanya ketika diluncurkan dari baris perintah . Melalui caveman debugging (mis., Pesan printf () yang buruk di semua tempat), saya telah menentukan metode pengujian di mana kode mengalami crash, meskipun sayangnya crash yang sebenarnya tampaknya terjadi di beberapa destruktor, karena jejak pesan terakhir yang saya lihat ada di perusak lain yang mengeksekusi dengan bersih.
Ketika saya mencoba menjalankan program ini di dalam Visual Studio, itu tidak macet. Hal yang sama berlaku saat meluncurkan dari WinDbg.exe. Kecelakaan hanya terjadi saat meluncurkan dari baris perintah. Ini terjadi di bawah Windows Vista, btw, dan sayangnya saya tidak memiliki akses ke mesin XP sekarang untuk mengujinya.
Akan sangat baik jika saya bisa mendapatkan Windows untuk mencetak jejak tumpukan, atau sesuatu selain hanya menghentikan program seolah-olah telah keluar dengan bersih. Adakah yang punya saran tentang bagaimana saya bisa mendapatkan informasi yang lebih bermakna di sini dan semoga memperbaiki bug ini?
Sunting: Masalahnya memang disebabkan oleh larik di luar batas, yang saya jelaskan lebih lanjut di posting ini . Terima kasih semuanya atas bantuan Anda dalam menemukan masalah ini!
Jawaban:
Dalam 100% kasus yang pernah saya lihat atau dengar, di mana program C atau C ++ berjalan dengan baik di debugger tetapi gagal saat dijalankan di luar, penyebabnya telah menulis melewati akhir fungsi larik lokal. (Debugger menempatkan lebih banyak pada tumpukan, jadi Anda cenderung tidak menimpa sesuatu yang penting.)
sumber
Ketika saya mengalami masalah seperti ini sebelumnya umumnya disebabkan oleh inisialisasi variabel. Dalam mode debug, variabel dan pointer diinisialisasi ke nol secara otomatis tetapi dalam mode rilis tidak. Oleh karena itu, jika Anda memiliki kode seperti ini
int* p; .... if (p == 0) { // do stuff }
Dalam mode debug, kode dalam if tidak dijalankan tetapi dalam mode rilis p berisi nilai yang tidak ditentukan, yang kemungkinan tidak bernilai 0, sehingga kode dieksekusi sering menyebabkan crash.
Saya akan memeriksa kode Anda untuk variabel yang tidak diinisialisasi. Ini juga dapat diterapkan pada konten array.
sumber
Sejauh ini tidak ada jawaban yang mencoba memberikan gambaran umum yang serius tentang teknik yang tersedia untuk men-debug aplikasi rilis:
Rilis dan build Debug berperilaku berbeda karena berbagai alasan. Berikut ini ikhtisar yang bagus. Setiap perbedaan ini mungkin menyebabkan bug dalam build Rilis yang tidak ada di build Debug.
Kehadiran debugger juga dapat mengubah perilaku program , baik untuk rilis maupun versi debug. Lihat jawaban ini. Singkatnya, setidaknya Visual Studio Debugger menggunakan Debug Heap secara otomatis saat dilampirkan ke program. Anda dapat menonaktifkan heap debug dengan menggunakan variabel lingkungan _NO_DEBUG_HEAP. Anda dapat menentukan ini di properti komputer Anda, atau di Pengaturan Proyek di Visual Studio. Tindakan tersebut mungkin membuat error dapat direkonstruksi dengan debugger terpasang.
Lebih lanjut tentang men-debug korupsi heap di sini.
Jika solusi sebelumnya tidak berhasil, Anda perlu menangkap pengecualian yang tidak tertangani dan melampirkan debugger post-mortem saat crash terjadi. Anda dapat menggunakan misalnya WinDbg untuk ini, detail tentang debugger post-mortem yang tersedia dan instalasinya di MSDN
Anda dapat meningkatkan kode penanganan pengecualian dan jika ini adalah aplikasi produksi, Anda harus:
Sebuah. Instal penangan terminasi kustom menggunakan
std::set_terminate
Jika Anda ingin men-debug masalah ini secara lokal, Anda dapat menjalankan loop tanpa akhir di dalam pengendali terminasi dan menampilkan beberapa teks ke konsol untuk memberi tahu Anda bahwa
std::terminate
telah dipanggil. Kemudian pasang debugger dan periksa tumpukan panggilan. Atau Anda mencetak jejak tumpukan seperti yang dijelaskan dalam jawaban ini.Dalam aplikasi produksi, Anda mungkin ingin mengirim laporan kesalahan kembali ke rumah, idealnya bersama dengan dump memori kecil yang memungkinkan Anda menganalisis masalah seperti yang dijelaskan di sini.
b. Gunakan mekanisme penanganan pengecualian terstruktur Microsoft yang memungkinkan Anda menangkap pengecualian perangkat keras dan perangkat lunak. Lihat MSDN . Anda bisa menjaga bagian dari kode Anda menggunakan SEH dan menggunakan pendekatan yang sama seperti di a) untuk men-debug masalah. SEH memberikan lebih banyak informasi tentang pengecualian yang terjadi yang dapat Anda gunakan saat mengirim laporan kesalahan dari aplikasi produksi.
sumber
Hal-hal yang harus diperhatikan:
Array overruns - debugger studio visual menyisipkan padding yang dapat menghentikan error.
Kondisi balapan - apakah Anda memiliki beberapa utas yang terlibat jika demikian, kondisi balapan banyak yang hanya muncul ketika aplikasi dijalankan secara langsung.
Menautkan - apakah versi rilis Anda menggunakan pustaka yang benar.
Hal untuk dicoba:
Minidump - sangat mudah digunakan (lihat saja di msdn) akan memberi Anda crash dump lengkap untuk setiap utas. Anda hanya memuat output ke studio visual dan seolah-olah Anda sedang melakukan debug pada saat crash.
sumber
Anda dapat mengatur WinDbg sebagai debugger postmortem. Ini akan meluncurkan debugger dan melampirkannya ke proses saat crash terjadi. Untuk menginstal WinDbg untuk debugging postmortem, gunakan opsi / I (perhatikan itu dikapitalisasi ):
Lebih lengkapnya di sini .
Mengenai penyebabnya, kemungkinan besar itu adalah variabel yang disatukan seperti yang disarankan jawaban lain.
sumber
Setelah berjam-jam debugging, akhirnya saya menemukan penyebab masalahnya, yang memang disebabkan oleh buffer overflow, menyebabkan perbedaan single byte:
char *end = static_cast<char*>(attr->data) + attr->dataSize;
Ini adalah kesalahan tiang pagar (kesalahan off-by-one) dan telah diperbaiki oleh:
char *end = static_cast<char*>(attr->data) + attr->dataSize - 1;
Yang aneh adalah, saya melakukan beberapa panggilan ke _CrtCheckMemory () di sekitar berbagai bagian kode saya, dan mereka selalu mengembalikan 1. Saya dapat menemukan sumber masalah dengan menempatkan "return false;" panggilan dalam kasus uji, dan akhirnya menentukan melalui trial-and-error di mana kesalahan itu.
Terima kasih semuanya atas komentar Anda - Saya belajar banyak tentang windbg.exe hari ini! :)
sumber
Meskipun Anda telah membuat exe sebagai versi rilis, Anda masih dapat menghasilkan file PDB (Program database) yang memungkinkan Anda untuk menumpuk pelacakan, dan melakukan pemeriksaan variabel dalam jumlah terbatas. Dalam pengaturan build Anda, ada opsi untuk membuat file PDB. Nyalakan dan tautkan kembali. Kemudian coba jalankan dari IDE terlebih dahulu untuk melihat apakah Anda mengalami error. Jika demikian, maka bagus - Anda siap untuk melihat berbagai hal. Jika tidak, maka saat menjalankan dari baris perintah Anda dapat melakukan salah satu dari dua hal:
Saat diminta untuk menunjuk ke file PDB, telusuri untuk menemukannya. Jika PDB diletakkan di folder keluaran yang sama dengan EXE atau DLL Anda, mereka mungkin akan diambil secara otomatis.
PDB menyediakan tautan ke sumber dengan informasi simbol yang cukup untuk memungkinkan melihat jejak tumpukan, variabel, dll. Anda dapat memeriksa nilai seperti biasa, tetapi perlu diketahui bahwa Anda bisa mendapatkan pembacaan yang salah karena pengoptimalan hanya dapat berarti muncul di register, atau sesuatu terjadi dalam urutan yang berbeda dari yang Anda harapkan.
NB: Saya mengasumsikan lingkungan Windows / Visual Studio di sini.
sumber
Crash seperti ini hampir selalu disebabkan karena IDE biasanya akan menyetel konten variabel yang tidak diinisialisasi menjadi nol, null, atau nilai 'masuk akal' lainnya, sedangkan saat menjalankan native, Anda akan mendapatkan sampah acak apa pun yang diambil sistem.
Oleh karena itu, kesalahan Anda hampir pasti karena Anda menggunakan sesuatu seperti Anda menggunakan penunjuk sebelum diinisialisasi dengan benar dan Anda lolos begitu saja di IDE karena tidak menunjuk ke mana pun yang berbahaya - atau nilainya ditangani oleh Anda. pengecekan kesalahan - tetapi dalam mode rilis itu melakukan sesuatu yang buruk.
sumber
Untuk memiliki dump kerusakan yang dapat Anda analisis:
Anda juga harus memeriksa alat di alat Debugging untuk windows . Anda dapat memantau aplikasi dan melihat semua peluang pengecualian pertama yang sebelum pengecualian kesempatan kedua Anda.
Semoga membantu ...
sumber
Cara yang bagus untuk men-debug error seperti ini adalah dengan mengaktifkan pengoptimalan untuk build debug Anda.
sumber
Suatu ketika saya mengalami masalah ketika aplikasi berperilaku mirip dengan milik Anda. Ternyata itu adalah buffer yang dibanjiri sprintf. Secara alami, ini bekerja ketika dijalankan dengan debugger terpasang. Apa yang saya lakukan, adalah menginstal filter pengecualian yang tidak tertangani ( SetUnhandledExceptionFilter ) di mana saya hanya memblokir tanpa batas (menggunakan WaitForSingleObject pada pegangan palsu dengan nilai batas waktu INFINITE).
Jadi Anda bisa melakukan sesuatu di sepanjang baris:
Saya kemudian memasang debugger setelah bug terwujud (program gui berhenti merespons).
Kemudian Anda dapat mengambil dump dan mengerjakannya nanti:
Atau langsung debug. Cara paling sederhana adalah melacak di mana konteks prosesor telah disimpan oleh mesin penanganan pengecualian waktu proses:
Perintah akan mencari ruang alamat tumpukan untuk catatan KONTEKS asalkan panjang pencarian. Saya biasanya menggunakan sesuatu seperti 'l? 10000' . Catatan, jangan gunakan angka besar yang tidak biasa karena rekaman yang Anda kejar biasanya dekat dengan bingkai filter pengecualian yang tidak ditangani. 1003f adalah kombinasi dari flag (saya yakin ini sesuai dengan CONTEXT_FULL) yang digunakan untuk menangkap status prosesor. Pencarian Anda akan terlihat seperti ini:
Setelah Anda mendapatkan hasil kembali, gunakan alamat di perintah cxr:
Ini akan membawa Anda ke KONTEKS baru ini, tepat pada saat kerusakan (Anda akan mendapatkan pelacakan tumpukan tepat pada saat aplikasi Anda mogok). Selain itu, gunakan:
untuk mengetahui dengan tepat pengecualian mana yang terjadi.
Semoga membantu.
sumber
Terkadang ini terjadi karena Anda telah memasukkan operasi penting di dalam makro "assert". Seperti yang Anda ketahui, "assert" mengevaluasi ekspresi hanya pada mode debug.
sumber
Berkenaan dengan masalah Anda mendapatkan informasi diagnostik, apakah Anda sudah mencoba menggunakan adplus.vbs sebagai alternatif untuk WinDbg.exe? Untuk melampirkan ke proses yang sedang berjalan, gunakan
Atau untuk memulai aplikasi jika crash terjadi dengan cepat:
Info lengkap tentang adplus.vbs dapat ditemukan di: http://support.microsoft.com/kb/286350
sumber
Ntdll.dll dengan debugger terpasang
Satu perbedaan kecil yang diketahui antara meluncurkan program dari IDE atau WinDbg sebagai kebalikan dari meluncurkannya dari baris perintah / desktop adalah bahwa saat meluncurkan dengan debugger terpasang (yaitu IDE atau WinDbg) ntdll.dll menggunakan implementasi heap berbeda yang melakukan sedikit validasi pada alokasi / pembebasan memori.
Anda dapat membaca beberapa informasi yang relevan di breakpoint pengguna yang tidak terduga di ntdll.dll . Salah satu alat yang mungkin dapat membantu Anda mengidentifikasi masalah adalah PageHeap.exe .
Analisis kerusakan
Anda tidak menulis apa "kecelakaan" yang Anda alami. Setelah program macet dan menawarkan Anda untuk mengirim informasi kesalahan ke Microsoft, Anda harus dapat mengklik informasi teknis dan untuk memeriksa setidaknya kode pengecualian, dan dengan sedikit usaha Anda bahkan dapat melakukan analisis post-mortem (lihat Heisenbug : Program WinApi macet di beberapa komputer) untuk instruksi)
sumber
Vista SP1 sebenarnya memiliki generator crash dump yang sangat bagus yang dibangun ke dalam sistem. Sayangnya, ini tidak diaktifkan secara default!
Lihat artikel ini: http://msdn.microsoft.com/en-us/library/bb787181(VS.85).aspx
Manfaat dari pendekatan ini adalah tidak ada perangkat lunak tambahan yang perlu diinstal pada sistem yang terpengaruh. Pegang dan robek, sayang!
sumber
Menurut pengalaman saya, yang paling banyak adalah masalah kerusakan memori.
Sebagai contoh :
char a[8]; memset(&a[0], 0, 16); : /*use array a doing some thing */
sangat mungkin untuk menjadi normal dalam mode debug ketika seseorang menjalankan kode.
Tapi dalam rilisnya, itu akan / mungkin crash.
Bagi saya, mencari-cari di mana memori sudah tidak terikat terlalu sulit.
Gunakan beberapa alat seperti Visual Leak Detector (windows) atau valgrind (linux) adalah pilihan yang lebih bijak.
sumber
Saya telah melihat banyak jawaban yang benar. Namun, tidak ada yang membantu saya. Dalam kasus saya, ada penggunaan yang salah dari instruksi SSE dengan memori yang tidak selaras . Lihatlah perpustakaan matematika Anda (jika Anda menggunakannya), dan coba nonaktifkan dukungan SIMD, kompilasi ulang dan rekonstruksi crash.
Contoh:
Sebuah proyek menyertakan mathfu , dan menggunakan kelas dengan vektor STL: std :: vector <mathfu :: vec2> . Penggunaan seperti itu mungkin akan menyebabkan crash pada saat konstruksi item mathfu :: vec2 karena pengalokasi default STL tidak menjamin diperlukan penyelarasan 16-byte. Dalam hal ini untuk membuktikan idenya, seseorang dapat menentukan
#define MATHFU_COMPILE_WITHOUT_SIMD_SUPPORT 1
sebelum setiap penyertaan mathfu , mengkompilasi ulang dalam konfigurasi Rilis dan memeriksa lagi.The Debug dan RelWithDebInfo konfigurasi bekerja dengan baik untuk proyek saya, tapi bukan Rilis satu. Alasan di balik perilaku ini mungkin karena debugger memproses permintaan alokasi / deallocation dan melakukan beberapa pembukuan memori untuk memeriksa dan memverifikasi akses ke memori.
Saya mengalami situasi di lingkungan Visual Studio 2015 dan 2017.
sumber
Sesuatu yang mirip terjadi pada saya sekali dengan GCC. Ternyata itu adalah pengoptimalan yang terlalu agresif yang hanya diaktifkan saat membuat rilis final dan bukan selama proses pengembangan.
Sejujurnya itu adalah kesalahan saya, bukan gcc, karena saya tidak memperhatikan bahwa kode saya mengandalkan fakta bahwa pengoptimalan tertentu tidak akan dilakukan.
Saya membutuhkan banyak waktu untuk melacaknya dan saya datang ke sana karena saya bertanya di newsgroup dan seseorang membuat saya memikirkannya. Jadi, izinkan saya membalas budi kalau-kalau ini terjadi pada Anda juga.
sumber
Saya telah menemukan artikel ini berguna untuk skenario Anda. ISTR opsi kompiler agak ketinggalan zaman. Lihat di sekitar opsi proyek Visual Studio Anda untuk melihat cara menghasilkan file pdb untuk rilis build Anda, dll.
sumber
Sangat mencurigakan bahwa ini akan terjadi di luar debugger dan bukan di dalam; berjalan di debugger biasanya tidak mengubah perilaku aplikasi. Saya akan memeriksa perbedaan lingkungan antara konsol dan IDE. Juga, jelas, kompilasi rilis tanpa pengoptimalan dan dengan informasi debug, dan lihat apakah itu memengaruhi perilaku. Terakhir, periksa alat debugging post-mortem yang disarankan orang lain di sini, biasanya Anda bisa mendapatkan petunjuk dari mereka.
sumber
Debugging rilis build bisa sangat merepotkan karena pengoptimalan yang mengubah urutan baris kode Anda tampaknya akan dieksekusi. Ini benar-benar bisa membingungkan!
Satu teknik untuk setidaknya mempersempit masalah adalah dengan menggunakan MessageBox () untuk menampilkan pernyataan cepat yang menyatakan bagian mana dari program kode Anda harus ("Memulai Foo ()", "Memulai Foo2 ()"); mulailah menempatkannya di bagian atas fungsi di area kode yang Anda curigai (apa yang Anda lakukan saat macet?). Saat Anda dapat mengetahui fungsi mana, ubah kotak pesan menjadi blok kode atau bahkan baris individual dalam fungsi itu hingga Anda mempersempitnya menjadi beberapa baris. Kemudian Anda dapat mulai mencetak nilai variabel untuk melihat statusnya pada titik kerusakan.
sumber
Coba gunakan _CrtCheckMemory () untuk melihat status memori yang dialokasikan. Jika semuanya berjalan dengan baik, _CrtCheckMemory mengembalikan TRUE , jika tidak FALSE .
sumber
Anda dapat menjalankan perangkat lunak Anda dengan Bendera Global diaktifkan (Lihat di Alat Debugging untuk Windows). Ini akan sangat sering membantu menyelesaikan masalah.
sumber
Buat program Anda menghasilkan mini dump saat pengecualian terjadi, lalu buka di debugger (misalnya, di WinDbg). Fungsi utama yang harus dilihat: MiniDumpWriteDump, SetUnhandledExceptionFilter
sumber
Inilah kasus yang saya miliki yang mungkin dianggap instruktif. Itu hanya macet saat rilis di Qt Creator - bukan di debug. Saya menggunakan file .ini (karena saya lebih suka aplikasi yang dapat disalin ke drive lain, vs. yang kehilangan pengaturannya jika Registry rusak). Ini berlaku untuk semua aplikasi yang menyimpan pengaturannya di bawah pohon direktori aplikasi. Jika build debug dan rilis berada di bawah direktori yang berbeda, Anda juga dapat memiliki pengaturan yang berbeda di antara keduanya. Saya memiliki preferensi yang diperiksa di salah satu yang tidak diperiksa di yang lain. Ternyata itulah sumber kecelakaan saya. Untung saya menemukannya.
Saya benci untuk mengatakannya, tapi saya hanya mendiagnosis crash di MS Visual Studio Community Edition; setelah VS diinstal, membiarkan aplikasi saya crash di Qt Creator, dan memilih untuk membukanya di debugger Visual Studio . Meskipun aplikasi Qt saya tidak memiliki info simbol, ternyata perpustakaan Qt memiliki beberapa. Itu membawa saya ke garis yang menyinggung; karena saya bisa melihat metode apa yang dipanggil. (Namun, menurut saya Qt adalah kerangka kerja LGPL yang nyaman, kuat, & lintas platform.)
sumber
Saya punya masalah ini juga. Dalam kasus saya, mode RELEASE memiliki msvscrtd.dll dalam definisi linker. Kami menghapusnya dan masalah teratasi.
Alternatifnya, menambahkan / NODEFAULTLIB ke argumen baris perintah linker juga menyelesaikan masalah.
sumber
Saya mengalami kesalahan ini dan vs macet bahkan ketika mencoba! Bersih! proyek saya. Jadi saya menghapus file obj secara manual dari direktori Rilis, dan setelah itu dibangun dengan baik.
sumber
Saya setuju dengan Rolf. Karena reproduktifitas sangat penting, Anda tidak boleh memiliki mode non-debug. Semua bangunan Anda harus dapat di-debug. Memiliki dua target untuk men-debug lebih dari dua kali lipat beban debugging Anda. Kirimkan saja versi "mode debug", kecuali jika tidak dapat digunakan. Dalam hal ini, buat itu bisa digunakan.
sumber