Öbek bozulması hataları nasıl ayıklanır?


166

Visual Studio 2008 altında bir (yerel) çok iş parçacıklı C ++ uygulamasında hata ayıklama. Görünüşe göre rastgele durumlarda, ben bir "Windows bir kırılma noktası tetikledi ..." hatası bunun bir bozukluğu nedeniyle olabilir yığın. Kısa süre sonra çökmesi muhtemel olsa da, bu hatalar her zaman uygulamayı hemen kilitlemez.

Bu hatalarla ilgili en büyük sorun, sadece yolsuzluk gerçekleştikten sonra ortaya çıkmalarıdır, bu da onları özellikle çok iş parçacıklı bir uygulamada izlemeyi ve hata ayıklamayı çok zorlaştırır.

  • Ne tür şeyler bu hatalara neden olabilir?

  • Onları nasıl ayıklayabilirim?

İpuçları, araçlar, yöntemler, aydınlanmalar ... açıktır.

Yanıtlar:


129

Windows için Hata Ayıklama Araçları ile birlikte Uygulama Doğrulayıcı inanılmaz bir kurulum. Her ikisini de Windows Sürücü Kiti'nin veya daha hafif Windows SDK'sının bir parçası olarak alabilirsiniz . ( Yığın bozulması sorunuyla ilgili daha önceki bir soruyu araştırırken Application Verifier hakkında bilgi edindim.) Geçmişte de BoundsChecker ve Insure ++ (diğer yanıtlarda belirtilmiştir) kullandım, ancak Uygulama Doğrulayıcı'da ne kadar işlevsellik olduğuna şaşırdım.

Elektrikli Çit ("efence" olarak da bilinir), dmalloc , valgrind ve benzerlerinin hepsi bahsetmeye değer, ancak bunların çoğu Windows * 'dan nix altında çalışmak çok daha kolaydır. Valgrind gülünç derecede esnektir: Büyük sunucu yazılımlarını kullanarak birçok yığın sorunu ayıkladım.

Her şey başarısız olduğunda, kendi global operatörünüze yeni / delete ve malloc / calloc / realloc aşırı yükleri sağlayabilirsiniz - bunun nasıl yapılacağı derleyiciye ve platforma bağlı olarak biraz değişecektir - ve bu biraz yatırım olacaktır - ancak uzun vadede ödeyebilir. Arzu edilen özellik listesi, dmalloc ve elektrik çitinden ve şaşırtıcı derecede mükemmel kitap Katı Kod Yazma'dan tanıdık görünmelidir :

  • nöbetçi değerleri : her bir tahsisattan önce ve sonra biraz daha fazla boşluk bırakın, maksimum hizalama gerekliliğine uyun; sihirli sayılarla doldur (arabellek taşmalarını ve taşmalarını ve ara sıra "çılgın" işaretçiyi yakalamaya yardımcı olur)
  • tahsis dolgusu : yeni ayırmaları sihirli olmayan bir 0 değeriyle doldurun - Visual C ++ bunu Debug derlemelerinde sizin için zaten yapacak (başlatılmamış varların kullanımını yakalamaya yardımcı olur)
  • boş dolgu : serbest bırakılmış belleği, 0 durumunda sihirli olmayan bir değerle doldurun, çoğu durumda kayıttan çıkarılırsa bir segfaultu tetiklemek için tasarlanmıştır (sarkan işaretçileri yakalamaya yardımcı olur)
  • gecikmeli boş : serbest bırakılmış hafızayı bir süreliğine yığına geri döndürmeyin, boşta dolu tutun, ancak mevcut değil (daha sarkan işaretçileri yakalamaya yardımcı olur, yaklaşık çift boşluğu yakalar)
  • izleme : bir tahsisin yapıldığı yeri kaydedebilmek bazen yararlı olabilir

Yerel homebrew sistemimizde (gömülü bir hedef için) izlemeyi diğer şeylerin çoğundan ayrı tuttuğumuza dikkat edin, çünkü çalışma zamanı yükü çok daha yüksektir.


Bu tahsis işlevlerini / operatörlerini aşırı yüklemek için daha fazla nedenle ilgileniyorsanız, "Genel operatörü yeni yüklemek ve silmek için herhangi bir neden var mı?" ; utanmaz öz-tanıtım bir yana, yığın yolsuzluk hatalarını izlemeye yardımcı olan diğer teknikleri ve diğer uygulanabilir araçları listeler.


MS'in kullandığı tahsis / serbest / çit değerleri ararken burada kendi cevabımı bulmaya devam ettiğim için, Microsoft dbgheap dolgu değerlerini kapsayan başka bir cevap .


3
Uygulama Doğrulayıcı hakkında kayda değer küçük bir şey: Uygulama Doğrulayıcı'nın sembollerini sembol arama yolunuzdaki microsoft sembol sunucusu sembollerinden önce kaydetmelisiniz, eğer bunu kullanırsanız ... Neden olduğunu anlamak için biraz arama yaptım! ihtiyaç duyduğu sembolleri bulmak.
leander

Uygulama Doğrulayıcı çok yardımcı oldu ve bazı tahminlerle birlikte, sorunu çözmeyi başardım! Yararlı noktalar getirdiğin için çok ve herkese de çok teşekkürler.

Uygulama Doğrulayıcı'nın WinDbg ile kullanılması mı gerekiyor, yoksa Visual Studio hata ayıklayıcısıyla mı çalışması gerekir? Kullanmaya çalışıyorum, ancak VS2012'de hata ayıkladığımda herhangi bir hata veya görünüşte bir şey yapmıyor.
Nathan Reed

@NathanReed: VS ile de çalıştığına inanıyorum - bkz. Msdn.microsoft.com/en-us/library/ms220944(v=vs.90).aspx - bu bağlantı VS2008 için olsa da, sonraki sürümlerden emin olabilirsiniz. Bellek biraz bulanık, ama "önceki soru" bağlantısında sorun vardı inanıyorum ben sadece Uygulama Verifier koştu ve seçenekleri kaydetti, programı koştu ve çöktü zaman ben hata ayıklamak için VS seçti. AV daha önce çöktü / iddia etti. ! Avrf komutu bildiğim kadarıyla WinDbg'ye özgüdür. Umarım diğerleri daha fazla bilgi sağlayabilir!
leander

Teşekkürler. Aslında asıl sorunumu çözdüm ve sonuçta yığın bozulması değil, başka bir şey olduğu ortaya çıktı, bu yüzden muhtemelen App Doğrulayıcı'nın neden bir şey bulamadığını açıklıyor. :)
Nathan Reed

35

Uygulamanız için Sayfa Yığını'nı etkinleştirerek birçok yığın bozulması sorununu tespit edebilirsiniz. Bunu yapmak için Windows için Hata Ayıklama Araçları'nın bir parçası olarak gelen gflags.exe dosyasını kullanmanız gerekir

Gflags.exe dosyasını çalıştırın ve yürütülebilir dosyanızın Görüntü dosyası seçeneklerinde "Sayfa Yığını Etkinleştir" seçeneğini işaretleyin.

Şimdi exe'nizi yeniden başlatın ve bir hata ayıklayıcıya ekleyin. Sayfa Yığını etkinleştirildiğinde, herhangi bir yığın bozulması olduğunda uygulama hata ayıklayıcıya bölünür.


evet ama bir kez ben bu fonksiyonu çağrı benim çağrı dökümü dökümü (bellek bozulması çökmesinden sonra) sonra: wow64! Wow64NotifyDebugger, ne yapabilirim? Uygulamamda neyin yanlış gittiğini hala bilmiyorum
Guillaume07

Sadece yığın yolsuzluk hata ayıklamak için gflags denedim, çok yararlı küçük bir araç, tavsiye. Gflags ile enstrüman kullanıldığında derhal hata ayıklayıcıya girecek olan boş hafızaya erişiyordum ... Handy!
Dave F

Harika bir araç! Sadece günlerce av yaptığım bir hata buldum, çünkü Windows yolsuzluğun adresini söylemiyor, sadece "bir şey" yanlış, ki bu gerçekten yararlı değil.
Devolus

Partiye biraz geç, ama önemli bir artış bellek kullanımı fark benim Sayfa Heap açıldığında hata ayıklama uygulaması. Ne yazık ki, öbek bozulması algılama tetiklenmeden önce (32bit) uygulama belleği yetersiz. Bu sorunun nasıl çözüleceği hakkında bir fikrin var mı?
uceumern

13

İşleri gerçekten yavaşlatmak ve çok sayıda çalışma zamanı denetimi gerçekleştirmek için main(), Microsoft Visual Studio C ++ 'da kendinizin veya eşdeğerinizin üstüne aşağıdakileri eklemeyi deneyin

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );


8

Ne tür şeyler bu hatalara neden olabilir?

Hafızalı yaramaz şeyler yapmak, örneğin, bir arabellek bittikten sonra yazmak veya bir arabellek yığınına serbest bırakıldıktan sonra yazmak.

Onları nasıl ayıklayabilirim?

Yürütülebilir programınıza otomatik sınır denetimi ekleyen bir araç kullanın: örn. Unix'te valgrind veya Windows'ta BoundsChecker (Wikipedia ayrıca Purify ve Insure ++ önerir) gibi bir araç.

Bunların uygulamanızı yavaşlatacağına dikkat edin, bu nedenle sizinki yumuşak gerçek zamanlı bir uygulamasa bunlar kullanılamaz olabilir.

Başka bir olası hata ayıklama yardımcısı / aracı MicroQuill'in HeapAgent'ı olabilir.


1
Uygulamayı hata ayıklama çalışma zamanı (/ MDd veya / MTd flag) ile yeniden oluşturmak benim ilk adımım olacak. Bunlar malloc ve ücretsiz olarak ek kontroller yaparlar ve çoğu zaman hataların yerini daraltmakta etkili olurlar.
Rus

MicroQuill'in HeapAgent: Çok fazla yazılı veya duyulmamış, ancak yığın bozulması için listenizde olması gerekir.
Samrat Patil

1
BoundsChecker bir duman testi olarak iyi çalışır, ancak o programı üretimde çalıştırmaya çalışırken altında bir program çalıştırmayı bile düşünmeyin. Yavaşlama, kullandığınız seçeneklere ve derleyici enstrümantasyon özelliğini kullanıp kullanmadığınıza bağlı olarak 60x ila 300x arasında herhangi bir yerde olabilir. Feragatname: Micro Focus için ürünü koruyan çocuklardan biriyim.
Rick Papo

8

Serbest hafızaya erişimi algılamaktan aldığım hızlı bir ipucu :

Bellek bloğuna erişen her ifadeyi kontrol etmeden hatayı hızlı bir şekilde bulmak istiyorsanız, bloğu boşalttıktan sonra bellek işaretçisini geçersiz bir değere ayarlayabilirsiniz:

#ifdef _DEBUG // detect the access to freed memory
#undef free
#define free(p) _free_dbg(p, _NORMAL_BLOCK); *(int*)&p = 0x666;
#endif

5

Yararlı buldum ve her zaman çalıştı en iyi araç (iyi kod yorumcular ile) kod inceleme olduğunu.

Kod inceleme dışında, ilk önce Page Heap denemek istiyorum . Sayfa Yığınının ayarlanması birkaç saniye alır ve şansla sorununuzu saptayabilir.

Sayfa Yığını ile şans yoksa , Microsoft'tan Windows için Hata Ayıklama Araçları'nı indirin ve WinDbg kullanmayı öğrenin. Maalesef size daha spesifik bir yardım sağlayamadı, ancak çok iş parçacıklı yığın yolsuzluğunu ayıklamak bilimden çok bir sanat. Google "WinDbg yığın bozulması" için ve konuyla ilgili birçok makale bulmalısınız.


4

Ayrıca, dinamik veya statik C çalışma zamanı kitaplığına bağlanıp bağlanmadığınızı kontrol etmek isteyebilirsiniz. DLL dosyalarınız statik C çalışma zamanı kitaplığına bağlanıyorsa, DLL dosyalarının ayrı yığınları vardır.

Bu nedenle, bir DLL dosyasında bir nesne oluşturup başka bir DLL dosyasında serbest bırakmaya çalışsaydınız, yukarıda gördüğünüz iletinin aynısını alırdınız. Bu sorun, farklı bir DLL dosyasında ayrılan boş bellekte başka bir yığın taşması sorusuna başvurulur .


3

Ne tür tahsis fonksiyonları kullanıyorsunuz? Son zamanlarda benzer bir hatayı Heap * tarzı ayırma işlevlerini kullanarak vurdu.

Yanlışlıkla HEAP_NO_SERIALIZEseçeneği ile yığın oluşturduğum ortaya çıktı . Bu, temel olarak Yığın işlevlerinin iş parçacığı güvenliği olmadan çalışmasını sağlar. Düzgün kullanılırsa bu bir performans geliştirmesidir, ancak çok iş parçacıklı bir programda HeapAlloc kullanıyorsanız hiç kullanılmamalıdır [1]. Bundan sadece bahsediyorum çünkü yayınınızda çok iş parçacıklı bir uygulama var. HEAP_NO_SERIALIZE ürününü herhangi bir yerde kullanıyorsanız, silin ve sorununuzu büyük olasılıkla düzeltir.

[1] Bunun yasal olduğu belirli durumlar vardır, ancak Heap * çağrılarını serileştirmenizi gerektirir ve genellikle çok iş parçacıklı programlar için durum böyle değildir.


Evet: uygulamanın derleyici / derleme seçeneklerine bakın ve C çalışma zamanı kitaplığının "çok iş parçacıklı" sürümüne bağlanmak üzere oluşturulduğundan emin olun.
ChrisW

HeapAlloc stil API'leri için @ChrisW bu farklıdır. Aslında bağlantı zamanı değil yığın oluşturma zamanında değiştirilebilen bir parametredir.
JaredPar

Ah. Bana göre OP'nin CRT'deki yığın hakkında değil, bu yığın hakkında konuşuyor olabileceği görülmedi.
ChrisW

@ChrisW, soru oldukça belirsiz ama sadece 1 hafta önce ayrıntılı olarak anlattığım soruna çarptım, bu yüzden aklımda taze.
JaredPar

3

Bu hatalar rastgele oluşursa, veri yarışlarıyla karşılaşma olasılığınız yüksektir. Lütfen kontrol edin: farklı dizilerden paylaşılan bellek işaretleyicilerini değiştiriyor musunuz? Intel Thread Checker çok iş parçacıklı programda bu tür sorunları tespit etmeye yardımcı olabilir.


1

Araç aramaya ek olarak, olası bir suçlu aramayı düşünün. Kullandığınız, belki de sizin tarafınızdan yazılmayan, çok iş parçacıklı bir ortamda çalışmak üzere tasarlanmamış ve test edilmemiş olabilecek herhangi bir bileşen var mı? Ya da basitçe bilmediğiniz biri böyle bir ortamda koşmuştur.

En son başıma geldiğinde, yıllarca toplu işlerden başarıyla kullanılan yerel bir paketti. Ancak bu şirkette ilk kez bir .NET web hizmetinden (çok iş parçacıklı) kullanılmıştı. İşte bu - kodun güvenli olduğu kodundan yalan söylediler.


1

_CrtSetDbgFlag : _CRTDBG_CHECK_ALWAYS_DF veya _CRTDBG_CHECK_EVERY_16_DF .. _CRTDBG_CHECK_EVERY_1024_DF için VC CRT Yığın Denetimi makrolarını kullanabilirsiniz .


0

Deneyimlerimi eklemek istiyorum. Son birkaç gün içinde, uygulamamda bu hatanın bir örneğini çözdüm. Benim özel durumumda, koddaki hatalar:

  • Üzerinde bir yineleme yaparken bir STL koleksiyonundan öğeleri kaldırma (Sanırım bu şeyleri yakalamak için Visual Studio'da hata ayıklama bayrakları var; Kod inceleme sırasında yakaladım)
  • Bu daha karmaşık, bunu adımlara bölerim:
    • Yerel bir C ++ iş parçacığından, yönetilen koda geri çağırın
    • Yönetilen arazide, geri aramanın Control.Invokeait olduğu yerel nesneyi saran yönetilen bir nesneyi arayın ve atın.
    • Nesne yerel iş parçacığının içinde hala hayatta olduğundan ( Control.Invokesona erene kadar geri arama çağrısında engellenmiş olarak kalacaktır ). Ben kullandığım açıklamak gerekir boost::thread, bu yüzden iş parçacığı işlevi olarak bir üye işlevi kullanın.
    • Çözüm : Control.BeginInvokeBunun yerine (GUI'm Winforms ile yapılır), böylece yerel iş parçacığı nesne yok edilmeden önce bitebilir (geri aramanın amacı iş parçacığının sona erdiğini ve nesnenin yok edilebileceğini bildirmektir).

0

Benzer bir sorunum vardı - ve oldukça rastgele ortaya çıktı. Belki yapı dosyalarında bir şey bozuktu, ama önce projeyi temizledikten sonra yeniden inşa ederek düzelttim.

Yani verilen diğer yanıtlara ek olarak:

Ne tür şeyler bu hatalara neden olabilir? Derleme dosyasında bozuk bir şey var.

Onları nasıl ayıklayabilirim? Projeyi temizleme ve yeniden inşa etme. Düzeltildiyse, sorun muhtemelen buydu.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.