Hata ayıklama belleği bozulması


23

Öncelikle, bunun mutlak bir cevabı olan mükemmel bir soru-cevap tarzı bir soru olmadığını fark ettim, ancak daha iyi çalışmasını sağlamak için herhangi bir ifade düşünemiyorum. Bunun mutlak bir çözüm olduğunu sanmıyorum ve bu, Stack Overflow yerine buraya göndermemin nedenlerinden biri.

Geçen ay boyunca, oldukça eski bir sunucu kodu (mmorpg) parçasını daha modern ve genişletilmesi / mod olması için yeniden yazıyordum. Ağ bölümü ile başladım ve benim için işleri yürütmek için bir 3. parti kütüphane (libevent) uyguladı. Tüm yeniden faktoring ve kod değişiklikleriyle bir yerde hafıza bozulmasına neden oldum ve nerede olduğunu bulmakta zorlanıyorum.

Bazı yükleri simüle etmek için ilkel botlar uygularken bile, çarpışmalara neden olamayacağım (bazı şeylere neden olan bir sorun çözdüm)

Şimdiye kadar denedim:

Bu cehennemden kurtulmak - Bir şey çökene kadar geçersiz bir yazı yazmaz (üretimde 1+ gün alabilir .. ya da sadece bir saat) bu beni gerçekten şaşırtıyor, kesinlikle bir noktada geçersiz belleğe erişir ve üzerine yazmaz şans mı? (Adres aralığını "yaymanın" bir yolu var mı?)

Kod Analizi araçları, yani kapsam ve kısaltma. Bazılarına işaret ettikleri halde .. kodda belirsizlik ve ileri vakalar ciddi bir şey yoktu.

İşlem gdb ile çökene kadar (undodb üzerinden) kayıt ve sonra geriye doğru çalışıyorum. Bu / sesler / olması gerektiği gibi olabilir, ancak ya otomatik tamamlama özelliğini kullanarak gdb'yi çökertiriyorum ya da çok fazla olası dal olduğundan (bir başkasının bozulmasına neden olan bir bozulma olduğu için) kaybolan bazı iç serbest yapıya giriyorum. ) üzerinde. Sanırım başlangıçta hangi göstergenin nereye tahsis edildiğine, hangi dallanma sorununu ortadan kaldıracağını görebilseydim iyi olurdu. Valgrind'i undodb ile çalıştıramıyorum ve normal gdb kaydı kullanışsız yavaş (eğer bu valgrind ile birlikte çalışıyorsa).

Kod incelemesi! Kendi başıma (iyice) ve bazı arkadaşlarımın kodumu gözden geçirmesine rağmen, yeterince iyiydi. Benimle bir kod inceleme / hata ayıklama yapmak için bir dev işe almayı düşünüyordum, ama çok fazla para harcayamayacağım ve az için çalışmaya istekli birini nerede arayacağımı bilemedim. konuyu bulamazsa veya hiç kimseyi nitelikli bulmazsa para.

Ayrıca şunu not etmeliyim: Genellikle tutarlı geri dönüşler alıyorum. Çarpmanın gerçekleştiği, çoğu zaman soket sınıfının bir şekilde bozulmuş olmasıyla ilgili birkaç yer var. Soket olmayan bir şeye işaret eden geçersiz bir işaretçi mi yoksa soket sınıfının kendisi (kısmen?) Anlamsızca yazılmaya başlıyor. Orada en çok kullanılanlardan biri olduğundan şüphelenmeme rağmen, en çok kullanılan kısımlardan biri, bu yüzden kullanılan ilk bozuk hafıza.

Bütün bu meselede yaklaşık 2 aydır beni meşgul etti (açık ve kapalı, daha fazla hobi projesi) ve huysuz IRL olduğum ve sadece pes etmeyi düşündüğüm noktaya gelmek beni gerçekten sinirlendiriyor. Sadece konuyu bulmak için ne yapmam gerektiğini düşünemiyorum.

Kaçırdığım faydalı teknikler var mı? Bununla nasıl başa çıkıyorsun? (Bu konuda çok fazla bilgi olmadığı için bu kadar yaygın olamaz .. ya da sadece gerçekten körüm?)

Düzenle:

Önemli olması durumunda bazı görüşler:

C ++ (11) kullanarak gcc 4.7 (debian wheezy tarafından sağlanan sürüm)

Kod temeli yaklaşık 150k satırdır.

David.pfx yayınına yanıt olarak düzenle: (yavaş yanıt için özür dilerim)

Örüntü aramak için çarpışmaların dikkatli kayıtlarını tutuyor musunuz?

Evet, hala etrafta yatan son kazaların dökümü var.

Birkaç yer gerçekten benzer mi? Ne şekilde

Eh, en son sürümde (kod eklediğim / kaldırdığımda veya ilgili yapıları değiştirdiğimde her şey değişiyor gibi görünüyor), her zaman bir öğe zamanlayıcı yönteminde yakalanır. Temel olarak bir öğenin süresi dolduktan sonra belirli bir süresi vardır ve müşteriye güncellenmiş bilgileri gönderir. Geçersiz soket işaretçisi (söyleyebildiğim kadarıyla hala geçerli) Player sınıfında, çoğunlukla bununla ilişkili olacaktı. Ayrıca, temizlik aşamasında, açıkça tahrip edilmemiş tüm statik sınıfları tahrip ettiği normal __run_exit_handlersgeri çekilmeden sonra (geriye dönük olarak) bir sürü çarpışma yaşıyorum . Çoğunlukla std::mapbir sınıfa dahil olmak, sanırım ilk ortaya çıkan şey bu.

Bozuk veriler neye benziyor? Sıfırları? Ascii? Desenler?

Henüz herhangi bir kalıp bulamadım, bana biraz rastgele görünüyor. Yolsuzluğun nerede başladığını bilmediğimden söylemek zor.

Öbekle ilgili mi?

Tamamen yığınla ilgili (Gcc'nin yığın korumasını etkinleştirdim ve hiçbir şey yakalamadım).

Yolsuzluktan sonra meydana geliyor free()mu?

Bu konuda biraz detaylandırmanız gerekecek. Etrafta serbestçe duran nesnelerin işaretçilerine sahip olmak mı demek istiyorsun? Nesne imha edildikten sonra her referansı boş bırakıyorum, bu yüzden bir şeyi kaçırmamışsam hayır. Bu olmasa da valgrind'da ortaya çıkmalı.

Ağ trafiği hakkında farklı bir şey var mı (arabellek boyutu, kurtarma döngüsü)?

Ağ trafiği ham verilerden oluşur. Böylece char dizileri, (u) intX_t veya daha karmaşık şeyler için paketlenmiş (dolguyu çıkarmak için) yapılar, her paket bir kimliği ve paket boyutunun kendisinden beklenen boyuta göre doğrulanmış bir başlıktan oluşur. Birkaç Mb boyutunda olan en büyük (dahili 'bootup' paketi, başlangıçta bir kez ateşlenmiş) sahip yaklaşık 10-60 bayttır.

Çok ve çok sayıda üretim iddiası var. Hasar yayılmadan önce erken ve öngörülebilir şekilde çarpın.

Bir zamanlar std::mapyolsuzlukla ilgili bir çöküş yaşadım , her varlığın “manzarasının” bir haritası vardı, onu görebilen her varlık var ve bunun tersi de var. Önüne 200 baytlık bir tampon ekledim ve sonra 0x33 ile doldurdum ve her erişimden önce kontrol ettim. Yolsuzluk sadece sihirli bir şekilde ortadan kayboldu, etrafta başka bir şeyi bozan bir şey taşımış olmalıyım.

Stratejik günlüğe kaydetme, yani tam olarak ne olduğunu tam olarak bilirsiniz. Bir cevaplamaya yaklaştıkça günlüklüğe ekleyin.

Uzatır .. çalışır.

Çaresizlik içinde, durumu kaydedebilir ve otomatik yeniden başlatabilir misiniz? Bunu yapan birkaç üretim yazılımını düşünebilirim.

Bunu biraz yapıyorum. Yazılım, ana bir "önbellek" işleminden ve bir şeyler almak ve kaydetmek için önbelleğe erişen diğer çalışanlardan oluşur. Bu yüzden kaza başına çok fazla ilerleme kaydetmiyorum, yine de tüm kullanıcıların bağlantısını keser ve bu kesinlikle bir çözüm değil.

Eşzamanlılık: iş parçacığı, yarış koşulları, vb

"Eşzamansız" sorguları yapmak için bir mysql dizisi var, her şey dokunulmamış olsa da ve sadece tüm kilidi olan işlevler aracılığıyla veri tabanı sınıfına bilgi paylaşıyor.

Kesmeler

30 saniye boyunca bir döngüyü tamamlamaması durumunda iptal eden, kilitlenmesini önlemek için bir kesme zamanlayıcısı vardır, ancak bu kod güvenli olmalıdır:

if (!tics) {
    abort();
} else
    tics = 0;

volatile int tics = 0;Her döngü tamamlandığında tikler artar. Eski kod da.

olaylar / geri aramalar / istisnalar: durumu veya yığını önceden tahmin edilemez bir şekilde bozma

Çok sayıda geri arama kullanılıyor (zaman uyumsuz ağ G / Ç, zamanlayıcılar), ancak kötü bir şey yapmamaları gerekiyor.

Olağandışı veri: olağandışı giriş verileri / zamanlama / durum

Bununla ilgili birkaç ileri dava yaşadım. Paketler hala işlenirken bir soketin çıkarılması, bir nullptr'ye ve bunun gibi sonuçlara erişilmesine neden oldu; (Yıkımın kendisi, yıkılan tüm nesneleri her döngüde silen bir döngü tarafından gerçekleştirilir)

Eşzamansız bir harici sürece bağımlılık.

Detaylandırmak ister misiniz? Bu durum biraz yukarıda, yukarıda belirtilen önbellek işlemidir. Kafamın üstünden hayal edebileceğim tek şey, yeterince hızlı bir şekilde bitirememesi ve çöp verilerini kullanmaması olabilirdi, ancak bu, ağ kullandığı için de geçerli değil. Aynı paket model.


7
Ne yazık ki, bu önemsiz olmayan C ++ uygulamalarında hella yaygındır. Kaynak kontrolü kullanıyorsanız, hangi kod değişikliğinin sorunun yardımcı olabileceğine karar vermek için çeşitli değişiklik kümelerini test etmek, ancak bu durumda uygun olmayabilir.
Telastyn

Evet, bu benim durumumda gerçekten mümkün değil. Temelde 2 ay boyunca tamamen ve tamamen kırılmaya çalışmaktan sonra biraz çalışma koduna sahip olduğum hata ayıklama aşamasına geçtim. Eski sistem gerçekten her şeyi kırmadan yeni tür esnek bir ağ kodumu uygulamak için izin vermedi.
Robin,

2
Bu noktada, her bir parçayı denemek ve izole etmek zorunda kalabilirsiniz. Çözümün her bir sınıfını / alt kümesini alın, çalışabilmesi için etrafında alay edin ve başarısız olan bölümü bulana kadar yaşayan cehennemi test edin.
Ampt

Artık çökmediğiniz sürece kod bölümlerini yorumlayarak başlayın.
cpp81

1
Valgrind, Coverity ve cppcheck'e ek olarak, Asan ve UBsan'ı test rejiminize eklemelisiniz. Kodunuz corss-platofrm ise, Microsoft'un Enterprise Analysis ( /analyze) ve Apple'ın Malloc ve Scribble korumalarını da ekleyin. Derleyici uyarıları tanısal olduğu ve zaman içinde daha iyi oldukları için mümkün olduğu kadar çok standart kullanarak mümkün olduğu kadar çok derleyici kullanmalısınız. Gümüş mermi yoktur ve tek beden herkese uymaz. Ne kadar çok araç ve derleyici kullanırsanız, kapsama alanı o kadar fazla olur, çünkü her bir takımın güçlü ve zayıf yönleri vardır.

Yanıtlar:


21

Bu zor bir problem, ama daha önce gördüğünüz kazalarda daha fazla ipucu bulabileceğinden şüpheleniyorum.

  • Örüntü aramak için çarpışmaların dikkatli kayıtlarını tutuyor musunuz?
  • Birkaç yer gerçekten benzer mi? Ne şekilde
  • Bozuk veriler neye benziyor? Sıfırları? Ascii? Desenler?
  • Çok parçacıklı herhangi bir konu var mı? Bu bir yarış durumu olabilir mi?
  • Öbekle ilgili mi? Yolsuzluk ücretsiz () sonrasında mı oluyor?
  • Yığınla mı ilgili? Yığın bozulur mu?
  • Sarkan bir referans mümkün mü? Gizemli bir şekilde değişen bir veri değeri?
  • Ağ trafiği hakkında farklı bir şey var mı (arabellek boyutu, kurtarma döngüsü)?

Benzer durumlarda kullandığımız şeyler.

  • Çok ve çok sayıda üretim iddiası var. Hasar yayılmadan önce erken ve öngörülebilir şekilde çarpın.
  • Çok ve çok sayıda muhafız var. Yerel değişkenlerden önce ve sonra ekstra veri öğeleri, nesneler ve mallocs () bir değere ayarlanmış ve daha sonra sık sık kontrol edilmiştir.
  • Stratejik günlüğe kaydetme, yani tam olarak ne olduğunu tam olarak bilirsiniz. Bir cevaplamaya yaklaştıkça günlüklüğe ekleyin.

Çaresizlik içinde, durumu kaydedebilir ve otomatik yeniden başlatabilir misiniz? Bunu yapan birkaç üretim yazılımını düşünebilirim.

Size yardımcı olabilirsek ayrıntı eklemek için çekinmeyin.


Sadece bu gibi belirsiz böceklerin bu kadar yaygın olmadığını ve (genellikle) onlara neden olabilecek pek çok şeyin olmadığını ekleyebilir miyim? İçerirler:

  • Eşzamanlılık: iş parçacığı, yarış koşulları, vb
  • Kesintiler / olaylar / geri aramalar / istisnalar: durumu veya yığını tahmin edilemez bir şekilde bozma
  • Olağandışı veriler: olağandışı girdi verileri / zamanlama / durum
  • Eşzamansız bir harici sürece bağımlılık.

Bunlar üzerinde durulacak kodun parçaları.


+1 Tüm iyi öneriler, özellikle iddialar, korumalar ve kayıtlar.
andy256

Cevabınıza bir cevap olarak sorumla ilgili daha fazla bilgi edindim. Bu aslında, henüz yoğun olarak bakmadığım kapanma sırasındaki kazaları düşündürdü, sanırım şimdilik bunun üzerine çıkacağım.
Robin,

5

Malloc / free hata ayıklama sürümünü kullanın. Onları sarın ve gerekirse kendiniz yazın. Çok eğlenceli!

Kullandığım sürüm, her tahsisattan önce ve sonra koruma baytları ekler ve serbest bırakılan parçalara karşı ücretsiz kontrollerin yapıldığı "tahsis edilmiş" bir liste tutar. Bu, çoğu arabellek taşması ve çoklu veya haydut "serbest" hataları yakalar.

En sinsi yolsuzluk kaynaklarından biri, serbest bırakıldıktan sonra bir yığın kullanmaya devam ediyor. Serbest, serbest bırakılan hafızayı bilinen bir desenle doldurmalıdır (geleneksel olarak, 0xDEADBEEF) Tahsis edilen yapıların "sihirli sayı" elementini içermesine ve liberal bir yapı kullanmadan önce uygun sihirli sayı kontrollerini içermesine yardımcı olur.


1
Valgrind çift serbestleri yakalamalı / serbest verileri kullanmalı, değil mi?
Robin,

Bu tür aşırı yüklemeleri yeni / silme için yazmak, çok sayıda bellek bozulması sorununu bulmama yardımcı oldu. Özellikle silme sırasında doğrulanan ve otomatik olarak beni hata ayıklayıcısına düşüren programın tetiklediği bir kırılma noktasına neden olan baytlar.
Emily L.,

3

Sorunuzda söylediklerinizi deşifre etmek için, size kesin bir cevap vermek mümkün değildir. Yapabileceğimiz en iyi şey, aranacak şeylerle ilgili önerilerde bulunmak, araç ve teknikler yapmaktır.

Bazı öneriler saf görünebilir, diğer dikişler daha uygulanabilir olabilir, ancak umarım biri takip edebileceğiniz bir düşünceyi tetikler. David.pfx'in cevabının sağlam tavsiye ve önerileri olduğunu söylemeliyim .

Belirtilerinden

  • Bana göre bir tampon taşması gibi geliyor.

  • ilgili bir sorun, doğrulanmamış soket verilerini bir abone veya anahtar vb.

  • Bir yerde global bir değişken kullanıyor olmanız, aynı isimde global ve yerel olmanız veya bir şekilde oyuncunun verileri diğeriyle etkileşime geçiyor olabilir mi?

Birçok hatada olduğu gibi, muhtemelen bir yerde geçersiz bir varsayımda bulunuyorsunuz. Ya da muhtemelen birden fazla. Birden fazla etkileşimde bulunan hataları tespit etmek zordur.

  • Her değişkenin bir açıklaması var mı? Ve bir geçerlilik iddiasını tanımlayabilir misiniz?
    Bunları eklemezseniz, her değişkenin doğru kullanıldığını görmek için kodu tarayın. Bu iddiayı anlamlı olduğu yere ekleyin.

  • Çok fazla iddiayı eklemek için öneri iyi bir şey: onları koymak için ilk yer her fonksiyon giriş noktasında. Argümanları ve ilgili herhangi bir küresel durumu doğrulayın.

  • Uzun süre çalışan / zaman uyumsuz / gerçek zamanlı kodları ayıklamak için çok fazla günlük kaydı kullanıyorum.
    Yine, her işlev çağrısına bir günlük yazma ekleyin.
    Günlük dosyaları çok büyük olursa, günlük işlevleri dosyaları / vb
    .
    Günlük dosyası bir hatanın nasıl yayıldığını gösterebilir. Bir kod parçası gecikmeli bir eylem bombası görevi gören doğru olmayan bir şey yaptığında kullanışlıdır.

Pek çok insan kendi evinde yetişen kayıt koduna sahiptir. Bir yerde eski bir C makro günlük sistemi var ve belki de bir C ++ sürümü ...


3

Diğer cevaplarda söylenen her şey çok alakalı. Boyacının kısmen bahsettiği önemli şeylerden biri, malloc / serbest sarımın faydalarının olmasıdır. Birkaçtan bahseder, ancak bunun için çok önemli bir hata ayıklama aracı eklemek isterim: Her malloc / free'yi harici bir dosyaya birkaç satırlık çağrı satırı (ya da umursanız tam arama) ile birlikte kaydedebilirsiniz. Dikkatliysanız, bunu oldukça hızlı bir şekilde kolayca yapabilir ve söz konusu olduğunda üretimde kullanabilirsiniz.

Benim tarif ettiğim şeye göre, kişisel tahminim, boş hafıza için bir işaretçi referansı tutuyor olabileceğinizi ve artık size ait olmayan bir işaretleyiciyi serbest bırakabileceğinizi ya da yazdığınız anlamına gelebilir. Yukarıdaki teknikle izlemek için bir boyut aralığı çıkarabilirseniz, kaydı önemli ölçüde daraltabilirsiniz. Aksi halde, hangi hafızanın bozulduğunu bulduğunuzda, kayıtlara oldukça kolay bir şekilde neden olan malloc / free desenini çözebilirsiniz.

Önemli bir not, bahsettiğiniz gibi, bellek düzenini değiştirmenin sorunu gizleyebileceğidir. Bu nedenle, günlüğünüzün (eğer yapabilirseniz!) Veya mümkün olduğunca az tahsis yapmaması çok önemlidir. Bu, hafıza ile ilgili ise tekrarlanabilirliğe yardımcı olacaktır. Ayrıca, konunun çok iş parçacığı ile ilgili olması halinde mümkün olduğu kadar hızlı olup olmadığını da yardımcı olacaktır.

Ayrıca, onları doğru bir şekilde kaydedebilmeniz için 3. parti kütüphanelerden ayırmaları yakalamanız da önemlidir. Nereden geleceğini asla bilemezsin.

Son bir alternatif olarak, her tahsis için en az 2 sayfa tahsis ettiğiniz ve serbest bıraktığınızda eşleştirebileceğiniz özel tahsisatçı da oluşturabilirsiniz (tahsisatı bir sayfa sınırına hizalayın, daha önce bir sayfa tahsis edin ve erişilemez olarak işaretleyin veya hizalayın. Bir sayfanın sonunda tahsis edilir ve sonra bir sayfa tahsis edilir ve işareti erişilebilir değildir). Bu sanal bellek adreslerini en az bir süre yeni tahsisler için tekrar kullanmamaya dikkat edin. Bu, sanal belleğinizi kendiniz yönetmeniz gerektiğini belirtir (saklayın ve istediğiniz gibi kullanın). Bunun performansınızı düşüreceğini ve kaç dağıtım yaptığınıza bağlı olarak önemli miktarda sanal bellek kullanabileceğini unutmayın. Bunu azaltmak için, 64bit'te çalışıp çalışmaya ihtiyacınız varsa ve / veya buna ihtiyaç duyan tahsisat aralıklarını (büyüklüğüne göre) azaltabilirsiniz. Valgrind bunu zaten çok iyi yapmış olabilir, ancak sorunu çözmeniz için çok yavaş olabilir. Bunu yalnızca birkaç boyut veya nesne için yapmak (hangisini biliyorsanız, yalnızca bu nesneler için özel ayırıcıyı kullanabilirsiniz) performansın minimum düzeyde etkilenmesini sağlayacaktır.


0

Kilitlendiği bellek adresinde bir saat noktası ayarlamayı deneyin. GDB geçersiz belleğe neden olan talimatı kesecektir. Sonra geri izlemeyle yolsuzluğa neden olan kodunuzu görebilirsiniz. Bu bir yolsuzluk kaynağı olmayabilir, ancak her yolsuzluktaki gözetleme noktasını tekrarlamak sorunun kaynağına neden olabilir.

Bu arada, soru C ++ etiketli olduğundan, referans sayısını koruyarak mülkiyeti önemseyen paylaşılan işaretçiler kullanmayı ve işaretçinin kapsam dışına çıktıktan sonra belleği güvenli bir şekilde silmeyi düşünün. Ancak, nadiren dairesel bağımlılık kullanımında kilitlenmeye neden olabileceği için bunları dikkatli kullanın.

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.