Guild Mark (Lonca Simgesi) Crash Fix
(fwrite / iDefaultWrite NULL buffer çökmesi + OOM koruması)
(fwrite / iDefaultWrite NULL buffer çökmesi + OOM koruması)
Belirti
MARK_SERVER görevini üstlenen game core'u rastgele görünen zamanlarda çöküyor. gdb ile core dump'a bakınca şu imza görülüyor:
Kod:
#0 fwrite (...) from /lib/libc.so.7
#1 iDefaultWrite (...)
#2 ilSaveL / ilSave (...)
#3 CGuildMarkImage::Save (this=..., c_szFileName=...) at MarkImage.cpp
#4 CGuildMarkManager::SaveMarkImage (...)
...
Yani çökme anında core, mark/mark_N.tga dosyasını diske yazmaya çalışıyor ve DevIL kütüphanesinin içinden
fwrite NULL buffer ile patlıyor.İkinci ipucu genelde çökmeden dakikalar önce syserr'de birikir:
Kod:
SYSERR: safe_create: calloc failed [12] Cannot allocate memory
Ve bir sonraki açılışta şu satır düşer (önceki çökmenin enkazı, yarım ya da bozuk kalan arma dosyası):
Kod:
SYSERR: Load: GuildMarkImage: mark/mark_0.tga cannot open file.
Nedeni:
- Süreç bellek sıkıntısına giriyor (leak, limit, ne sebeple olursa).
calloc failed [12]satırları bunun kanıtı. - Bu sırada bir oyuncu lonca arması yüklüyor / arma senkronu tetikleniyor. DevIL, 512x512x4 = 1 MB'lık imaj buffer'ını ayırmaya çalışıyor. Bellek yok. ilTexImage sessizce başarısız oluyor, imajın data pointer'ı NULL kalıyor.
- Kod hiçbir yerde bunu kontrol etmiyor; sıra
Save()'e gelince DevIL, NULL pointer'dan 1 MB okuyupfwriteile diske yazmaya kalkıyor. SEGFAULT, core düşüyor. - Bonus hasar: fwrite yarıda patladığı için mark_N.tga dosyası bozuk/eksik kalabiliyor... Sonraki açılışta "cannot open file".
Yani asıl hastalık bellek yetersizliği, ama çökme tamamen önlenebilir: DevIL çağrılarından önce imaj verisinin gerçekten var olduğunu kontrol etmek yetiyor. Aşağıdaki 4 guard bunu yapar. Bellek sıkışsa bile core artık düşmez, syserr'e anlamlı satır yazıp o işlemi atlar.
Fix - game/src/MarkImage.cpp (4 nokta)
1) Save() verisiz imajı kaydetme (çökmenin doğrudan önlemi)
ARAT:
Kod:
ilEnable(IL_FILE_OVERWRITE);
ilBindImage(m_uImg);
if (!ilSave(IL_TGA, (const ILstring)c_szFileName))
return false;
return true;
}
ŞÖYLE DEĞİŞTİR:
Kod:
ilEnable(IL_FILE_OVERWRITE);
ilBindImage(m_uImg);
if (NULL == ilGetData() || ilGetInteger(IL_IMAGE_SIZE_OF_DATA) <= 0)
{
sys_err("GuildMarkImage: no image data, save skipped (%s)", c_szFileName);
return false;
}
if (!ilSave(IL_TGA, (const ILstring)c_szFileName))
return false;
return true;
}
2) Build() malloc NULL kontrolü + hata dalında free
Orijinal kod malloc'un sonucunu kontrol etmeden memset yapıyor (OOM anında ikinci bir crash noktası) ve ilTexImage başarısız olursa buffer'ı sızdırıyor.
ARAT:
Kod:
BYTE * data = (BYTE *) malloc(sizeof(Pixel) * WIDTH * HEIGHT);
memset(data, 0, sizeof(Pixel) * WIDTH * HEIGHT);
if (!ilTexImage(WIDTH, HEIGHT, 1, 4, IL_BGRA, IL_UNSIGNED_BYTE, data))
{
sys_err("GuildMarkImage: cannot initialize image");
return false;
}
free(data);
ŞÖYLE DEĞİŞTİR:
Kod:
BYTE * data = (BYTE *) malloc(sizeof(Pixel) * WIDTH * HEIGHT);
if (NULL == data)
{
sys_err("GuildMarkImage: out of memory, cannot build %s", c_szFileName);
return false;
}
memset(data, 0, sizeof(Pixel) * WIDTH * HEIGHT);
if (!ilTexImage(WIDTH, HEIGHT, 1, 4, IL_BGRA, IL_UNSIGNED_BYTE, data))
{
sys_err("GuildMarkImage: cannot initialize image");
free(data);
return false;
}
free(data);
3-4) PutData() ve GetData() verisiz imajda piksel işlemi yapma
ilSetPixels / ilCopyPixels da NULL imaj datasıyla çağrılırsa aynı şekilde patlayabilir.ARAT:
Kod:
void CGuildMarkImage::PutData(UINT x, UINT y, UINT width, UINT height, void * data)
{
ilBindImage(m_uImg);
ilSetPixels(x, y, 0, width, height, 1, IL_BGRA, IL_UNSIGNED_BYTE, data);
}
void CGuildMarkImage::GetData(UINT x, UINT y, UINT width, UINT height, void * data)
{
ilBindImage(m_uImg);
ilCopyPixels(x, y, 0, width, height, 1, IL_BGRA, IL_UNSIGNED_BYTE, data);
}
ŞÖYLE DEĞİŞTİR:
Kod:
void CGuildMarkImage::PutData(UINT x, UINT y, UINT width, UINT height, void * data)
{
ilBindImage(m_uImg);
if (NULL == ilGetData())
{
sys_err("GuildMarkImage: no image data, PutData skipped");
return;
}
ilSetPixels(x, y, 0, width, height, 1, IL_BGRA, IL_UNSIGNED_BYTE, data);
}
void CGuildMarkImage::GetData(UINT x, UINT y, UINT width, UINT height, void * data)
{
ilBindImage(m_uImg);
if (NULL == ilGetData()) // OOM korumasi: hedef buffer'i sifirla ve cik
{
sys_err("GuildMarkImage: no image data, GetData zero-filled");
memset(data, 0, sizeof(Pixel) * width * height);
return;
}
ilCopyPixels(x, y, 0, width, height, 1, IL_BGRA, IL_UNSIGNED_BYTE, data);
}
Not: Bazı source'lardailCopyPixelssatırının sonunda görünmez bir tab karakteri olabiliyor; ARAT bloğu birebir eşleşmezse satırları tek tek bulup elle uygulayın. Ayrıca dosyada Korece (cp949) yorumlar varsa dosyayı UTF-8'e çevirmeyin, olduğu gibi düzenleyin.
Kurulum
- MarkImage.cpp'ye 4 eklemeyi yapın
- game core'u derleyin, çalışan binary'nin üstüne yazmayın: eskisini
mvile kenara alın, yenisini koyun, restart - Fix yalnızca MARK_SERVER görevini üstlenen core'da etkilidir (armalar tek core'dan servis edilir), yine de tüm kanallara aynı binary'yi dağıtın
Fix sonrası davranış
- Bellek sıkıştığında arma yükleme/kaydetme çökmek yerine o işlemi atlar; syserr'e
GuildMarkImage: no image data...satırı düşer. Bu satırı görürseniz bilin ki core bellek açlığında asıl leak'i çözmeniz için erken uyarıdır. - Önceki çökmelerden bozuk kalan mark_N.tga dosyaları sorun değildir: açılışta "cannot open file" der, dosyayı sıfırdan oluşturur.
Önemli: Bu fix semptomu (crash) kapatır, hastalığı (bellek tükenmesi) değil. syserr'decalloc failedgörüyorsanız süreç limitlerinizi (limits, datasize) ve memory leak ihtimalini ayrıca araştırın; RSS'i cron ile loglayıp büyüme eğrisini izlemek en pratik başlangıçtır.
İyi forumlar. Aynı imzalı (fwrite, iDefaultWrite, ilSave, GuildMarkImage::Save) çökme yaşayanlar için birebir çözümdür.
En Çok Reaksiyon Alan Mesajlar
Dipnot;
Gece alakasız bir core kapanması yaşarsanız ve core içerisini açtığınızda göreceğiniz şey anlamsız bu sorunu işaret eden çıktıysa o an böyle oluyorsunuz:
Öğeyi görmek için üye olmalısınız.
Dipnot;
Gece alakasız bir core kapanması yaşarsanız ve core içerisini açtığınızda göreceğiniz şey anlamsız bu sorunu işaret eden çıktıysa o an böyle oluyorsunuz:
Gece alakasız bir core kapanması yaşarsanız ve core içerisini açtığınızda göreceğiniz şey anlamsız bu sorunu işaret eden çıktıysa o an böyle oluyorsunuz:
Şu an konuyu görüntüleyenler (Toplam : 0, Üye: 0, Misafir: 0)
Benzer konular
- Cevaplar
- 12
- Görüntüleme
- 1K
- Cevaplar
- 3
- Görüntüleme
- 464
- Cevaplar
- 3
- Görüntüleme
- 148