mavzermete 1
mavzermete
Mt2Hizmet 1
Mt2Hizmet
noisiv 1
noisiv
Manwe Work 1
Manwe Work
Reklam vermek için turkmmo@gmail.com

Altın Konu Guild Mark (Lonca Simgesi) Crash Fix

  • Konuyu başlatan Konuyu başlatan Silverhand
  • Başlangıç tarihi Başlangıç tarihi
  • Cevaplar Cevaplar 5
  • Görüntüleme Görüntüleme 203
Guild Mark (Lonca Simgesi) Crash Fix
(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:

  1. Süreç bellek sıkıntısına giriyor (leak, limit, ne sebeple olursa). calloc failed [12] satırları bunun kanıtı.
  2. 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.
  3. Kod hiçbir yerde bunu kontrol etmiyor; sıra Save()'e gelince DevIL, NULL pointer'dan 1 MB okuyup fwrite ile diske yazmaya kalkıyor. SEGFAULT, core düşüyor.
  4. 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'larda ilCopyPixels satı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

  1. MarkImage.cpp'ye 4 eklemeyi yapın
  2. game core'u derleyin, çalışan binary'nin üstüne yazmayın: eskisini mv ile kenara alın, yenisini koyun, restart
  3. 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'de calloc failed gö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.

Şu an konuyu görüntüleyenler (Toplam : 0, Üye: 0, Misafir: 0)

Geri
Üst