QT segmentation fault hatası

@eminfedar @ibrahimcorut yardımınızın dokunacağını düşünüyorum. Eğitimini almadım kendi çapımda öğrenip bir şeyler yapmaya çalışıyorum. mamoo/mhss - mhss - Milis Linux Git Depo Sunucusu projemde mamoo/mhss - stokfrom.cpp at c8b6204ce9f406e799be9fc2eab1dfec4e0c0689 - mhss - Milis Linux Git Depo Sunucusu bu satırda segmentation fault hatası alıyorum. Yardımlarınız için şimdiden teşekkürler.

1 Beğeni

Selamün Aleyküm Kardeşim,
Sana yardımcı olmak isterdim ama ben Python ile çalışıyorum.
Bilmediğim, denemediğim bir konuda seni yanlış yönlendirme yapmak istemem.

2 Beğeni

Merhaba,

Debug sembolleriyle derleyip debugger (gdb) ile çalıştırmayı dener misiniz? Qt Creator ile geliştiriyorsanız, Configuration olarak debug seçmeniz derleme için yeterli olacaktır. Qt’un debug sembollerine de ihtiyacınız olabilir, ama ihtiyaç olmadığı sürece bunu düşünmenize gerek yok. Akşama doğru kişisel bilgisayarımda deneyebilirim.

Hangi Qt versiyonunu kullanıyorsunuz? Uygulamayı debug ederken yada çalıştırırken çevre değişkenlerine QT_LOGGING_RULES="*.debug=true ekleyip dener misiniz?

QT_LOGGING_RULES="*.debug=true"
1 Beğeni

Az önce denedim ve uygulamanız Qt 5.9.9 kullanarak çalıştı. Çalıştırmak için kullanıcı girişi kodunda değişiklikler yaptım. Kısacası o kısmı bypass ettim. Bahsettiğiniz satırda SIGSEGV oluşmadı. Ancak sorunun nasıl oluştuğunu mesajınızda anlatmadığınız için doğru yapıp yapmadığımı bilmiyorum; sadece “Stok Grupları” butonuna bastım ve çalıştı.

Bende veritabanı bulunmadığı halde uygulama “çalışıyor” ve bazı işlemleri gerçekleştirebiliyor gibi. Veritabanı oluşturmayı isteyip istemediğimi soruyor, evet dediğimde de oluşturmuyor. Belki veritabanıyla yapılan işlemlerde sorun oluşuyordur ve bu işlemlerin sonuçlarını kontrol etmeyi atlamış olabilirsiniz. Veritabanından okunan veri bozulmuştur yada tipler uyuşmuyor olabilir. Debugger altında incelemenizi tavsiye ediyorum.

1 Beğeni

@ismailp cevaplarınız için teşekkür ederim. QT 5.15.2 sürümünde geliştiriyorum. Postgresql sürümüm v13.3.
stokgrupform.exec() satırında formun yapıcı metoduna giriyor bütün işlemleri yapıyor ve formu yüklüyor. stokgrupform.exec() satırından çıkarken SIGSEGV hatası veriyor.
2021-11-02_21:07:58

yani patladığında zaten işlem bitmiş oluyor. Ben uygulamamda tüm veritabanı işlemlerini tek bir sınıf içerisinde yapmak istedim. Bu doğru bir programlama mıdır? Bir yerde mantık hatası yapıyorum ama bulamadım bir türlü. Aşağıda ki resimde görüldüğü gibi form yüklenmiş oluyor.
2021-11-02_21:11:25

Kodunuzun tamamını incelemedim. Veritabanı işlemlerini tek bir yerde toplamanız doğru bir yaklaşım. Ancak kaynakların kullanımıyla ilgili bazı sorunlar var.

Veritabanı gibi kaynakları kullanırken fonksiyonların dönüş değerlerini mutlaka kontrol etmelisiniz. Örneğin db.open() başarısız olduğunda veritabanıyla işlem gerçekleştiremeyeceksiniz ve bu duruma karşı hazırlıklı olmalısınız. Sıklıkla hata olup olmadığını lastError() ile kontrol etmeniz gerekli.

Veritabanı bağlantısıyla işiniz bittiğinde kapatmalısınız, ama her işlem öncesi bağlantı açıp sonra kapatmanız sizin uygulamanız için doğru gelmedi. Veritabanı bağlantısı açma işlemi uzun süren ve kaynak tüketen bir işlem. Ayrıca, Qt, sizin için bağlantı işini oldukça kolaylaştırıyor.

Qt Widgets ve SQL veritabanlarını birlikte kullanmadım, dolayısıyla Qt’un bu modülleriyle ilgili tecrübem yok. Qt Widgets en son 2006’da kullandım. QML, Quick, Core, GUI ve Test modülleriyle çok tecrübem var. Bunlara, bilhassa qtqml modülüne, yamalarım da var. Ancak ticari lisansımız olduğu için yamaları paylaşmadık. Çalıştığım şirkette dört yıl boyunca Qt modüllerinin güncellenmesinden ve lisans işlemlerinden sorumluydum. GitHub’ta da Qt ve Qt Creator ile ilgili birkaç repom var.

Qt dokümanlarına göre, veritabanı bağlantınızı programın başlarında bir yerde şu şekilde hazırlayabilirsiniz:

    QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL", "mhss_data");
    db.setHostName("localhost");
    db.setUserName("postgres");
    db.setPassword("postgres");
    if (!db.open())
        qFatal(db.lastError().text().toStdString().c_str());

Ben bunu main içerisinde yaptım. QSqlDatabase::addDatabase("QPSQL", "mhss_data") ile sizin veritabanı bağlantınız Qt tarafından global bir yerde saklanıyor. Bu bağlantıyı başlarda db.open() ile açıyorsunuz. Program sonunda bu bağlantı otomatik olarak kapanacak. Kendi veritabanı sınıfınızdaki db ise db = QSqlDatabase::database("mhss_data") şeklinde hazırlanıyor. Program başlangıcında bir defa QSqlDatabase::addDatabase ile ekleyip açtığınız bağlantıya QSqlDatabase::database fonksiyonuyla erişebiliyorsunuz ve açıp kapatmanıza gerek kalmıyor.

SIGSEGV sebebi, anladığım kadarıyla, Veritabani sınıfının sürekli olarak yeni bağlantı açması ve eski bağlantının otomatik olarak silinmesi. Ancak Stok Kartları tablosu, yani görünen küçük dialog değil de arkadaki büyük tablonun eski bağlantıyı kullanmaya çalışması. Çünkü setModel ile siz ona eski bağlantıyı kullanan bir model veriyorsunuz. Ben bu mesaja yaptığımı kısa yamayı ekledim. Yamaya bakarsanız, aç-kapa kısımlarını silmek sorunu çözmüşe benziyor.
0001-Fixed-segfault.patch.txt (10.3 KB)

1 Beğeni

@ismailp bey ilginiz için çok teşekkür ederim. Çok değerli bilgiler verdiniz. Sorunum çözüldü. Qt dökümanlarını okuyorum aslında ama ingilizcem iyi değil doğru anlayamamışım. Bu projenin arayüzüne ilk QML ile başladım. QML de ekran çözünürlüğüne göre arayüz çok güzel ayarlanıyor. İş tablolamaya gelince çok karışık geldi yapamadım bir türlü ve QWigdets’e döndüm.
Madem sizin gibi birini buldum bir soru daha sormak isterim. :slight_smile: QLocale ‘Turkey’,‘Turkish’ ayarlı olmasına rağmen .ToUpper() / .ToLower() fonksiyonları türkçe karakterlerlerde sorun çıkartıyor. Mesela “ismail” 'i büyük harfe çevirirken “ISMAIL” olarak çeviriyor. Qt4 ile fixlendiğini okudum ama aynı.

Bu konu son yanıttan 30 gün sonra otomatik olarak kapatıldı. Yeni yanıt girilmesine izin verilmiyor.

Rica ederim, yardımcı olabildiysem, ne mutlu.

QString::toUpper, sadece C locale ile çalışıyor, yani istediğinizi yapmıyor. QLocale::toUpper ise ICU (International Components for Unicode) kullanarak doğru çevrimi yapabiliyor. Bu da ICU desteğiyle derlenmiş bir Qt kurulumunda doğru çalışacağı anlamına geliyor. Eğer Qt, ICU desteğiyle derlenmediyse, QString::toUpper'ı çağırıyor, yani doğru çevrim gerçekleşmiyor. Kullandığınız Qt, ICU ile derlenmişse (resmi dağıtımlar öyleydi diye hatırlıyorum) çevrim doğru yapılamaz. Bence sisteminizde icu zaten var, ama emin olmak için sisteminize icu kurup yeniden deneyin.

Qt kütüphanelerini kaynak kodundan derleyebilirsiniz ve lisans uyumlu uygulamalarla dağıtabilirsiniz. Bu durumda libicu-dev kurmanız ve Qt derlerken configure aşamasında -icu şeklinde belirtmeniz yeterli olur.

#include <QDebug>
#include <QLocale>
#include <QString>

int main() {
  QLocale l(QLocale::Turkish, QLocale::Turkey);
  qDebug("upper: %s\n", qPrintable(l.toUpper(QString("ismail"))));
}

Resmi Qt dağıtımı, ICU kuruluysa (Debian 11) bu şekilde doğru çevrimi gerçekleştirebiliyor.

Kolay gelsin :slight_smile:

1 Beğeni

@ismailp bey tekrar merhaba :slight_smile: yeni bir sorunum var :neutral_face: Uygulamayı denemek için başka bir pc ye git klonlayıp qmake ile build ettim. make install ile kurdum. Bende sorunsuz çalışan uygulama yeni pc de sorunlu çalıştı.

Sorun;
satış ekranında sepete 2. bir kalem ürün eklendiğinde parçalama arızası verip sonlanıyor ama benim bilgisayarımda böyle bir hata vermiyor sorunsuz çalışıyor.

Projeyi build ettiğim pc de uygulamanın ikili dosyasına terminalden strip uygulama_adi komutunu uygularsam uygulama yine sorunsuz çalışıyor. strip uygulama_adi komutunu uyguladıktan sonra ikili dosya boyutu 8MB den 1.1MB düşüyor.

Benim bilgisayarımda QT Creator’dan test modunu debug dan release alırsam aynı sorunu bende de yapıyor. Bunun sebebi nedir? Kodumda mı hata var build konfigürasyonunda mı hata var anlayamadım. Yardımın için şimdiden teşekkür ederim.
oie_J2VSEs7FewLF

Ayrıca uygulama şuan market bilgisayarında ikiliye strip uygulama_adi uygulanmış şekilde sorunsuz çalışıyor. fakat gün sonunda uygulamanın ram tüketimi 2.5GB ~ 3GB arası oluyor. Bu çok kötü. Sebebi sanırım pointer kullanımından kaçınmam.

Sorunu çözdüm. Kontrol metotlarımdan biri sorunluymuş. Debug modunda çalışıp Release modunda neden çalışmadı ilginç.

Merhaba!

Bu sıralar çok yoğun olduğum için cevap veremedim. Az önce denedim, bende sorun olmadı. Belki daha önceki bir commit’i denemem gerekiyordu.

Debug ve Release modları arasında bir kaç fark var, ancak sizin açınızdan en öne çıkanı optimizasyon seviyesindeki farktır. Debug modunda debug sembolleri mevcuttur ve optimizasyon ya yapılmaz yada debug sembollerini etkilemeyecek çok basit transformasyonlar yapılır. Bu mod hata ayıklamak için idealdir.

Kodunuz, tanımsız davranış (undefined behavior) tetiklemiş olabilir. Bu durumda, derleyici içerisindeki optimizer beklemediğiniz bir değişiklik yapıp derleyicinin hatalı, eksik, yada farklı bir kod üretmesine neden olabildiği gibi tanımsız davranışa bağlı kodu tamamen silebilir (gözardı edebilir). Çünkü doğru programlarda tanımsız davranış olamaz ve optimizer, gözlemlenebilir sonucu değiştirmeyen her tür değişikliği yapabilir. Buna “as-if rule” diyoruz. Derleyicinin ürettiği makine kodu, sizin kaynak kodunda gördüğünüz sırada yada tahmin ettiğiniz şekilde olmayabilir.

Release modunda ise optimizasyon seviyesi artar ve bu mod üretimde kullanmak için idealdir. Günlük hayatta kullandığımız yazılımların tamamı release’e denk gelen ayarlarla derlenir. Optimizasyon seviyesi, optimizer’a ne kadar agresif olabileceğini söyler. Bu optimizasyon mutlaka hızı artırmaya yönelik değil, uygulama boyutunu küçük tutmaya göre de olabilir. Çok sayıda optimizasyon yöntemi vardır ve her seviyede belirli bir sayı ve sırada optimizasyonlar uygulanır. Belirttiğim gibi, kodunuzda undefined behavior varsa, optimizer tarafından çalıştırılan transformasyonlar sizin kodunuzu beklediğiniz gibi yorumlamayabilirler ve sonuçta uygulama en iyi ihtimalle çöker. Uygulama çökmeyip hatalı sonuç verebilir, her defasında farklı davranabilir, hatta doğru sonuç da verebilir. Ne olacağını tahmin etmeniz mümkün olmadığı için bu çok tehlikeli bir durumdur ve kaçınılmalıdır.

Deyinmeden geçmemeliyim; bir yazılımı geliştirmeye mutlaka testlerini yazarak başlayın. Buna test-driven development diyoruz. Önce test yazıyoruz, test başarısız oluyor, kodu yazıyoruz ve test geçene kadar kodu iyileştiriyoruz. Uygulama bu şekilde gelişiyor. Hataları yakalamanız çok daha hızlı olacaktır. Ayrıca fonksiyonlarınızda bazı ön koşulları ve çıkış koşullarını da Q_ASSERT ile kontrol edebilirsiniz. Mesela veritabanına erişmeden önce Q_ASSERT(db.isOpen());, veritabanı sorgunuzun ardından da Q_ASSERT(!db.lastError().isValid());. Sonuncuda db yerine sorgu nesnesini de kullanmanız gerekebilir, ondan emin değilim. Bu şekilde debug sırasında bazı sorunları yakalamanız kolaylaşır.

C ve C++ uygulamarındaki tanımsız davranışları tespit için çeşitli araçlar kullanabiliriz. Kaynak kodu elinizdeyse ve derlemesi basitse, sanitizer kullanabilirsiniz. Bu araçlardan en iyi verimi almak için testlerle birlikte kullanın yada test ederken kullanın. Kaynak kodu elinizde değilse yada derlemek mümkün/pratik değilse, valgrind kullanabilirsiniz.

Birkaç çeşit sanitizer var. Sizin en işinize yarayacak olanlar address ve undefined. Qt uygulamalarını Release modunda “sanitize” etmek için Release modundan türeyen yeni bir config oluşturun (isterseniz Release ile devam edin, seçim sizin). Proje ayarlarından qmake’e ek olarak şunları verin:
CONFIG+=sanitizer CONFIG+=sanitize_address CONFIG+=sanitize_undefined
Ardından uygulamanızı yeniden derleyin ve çalıştırın. Eğer sorun doğrudan sizin kodunuzdaysa, bu araçlar sorunu yakalayıp detaylı bir rapor veriyor. Bu raporlar çoğu zaman doğru ve gerçek bir sorunu işaret ederler. Çünkü sanitizer aslında derleyici tarafından derleme işlemi sırasında hata tespiti için, üretilen makine koduna bazı kontroller ekler. Sizin kodunuzla beraber bu kontroller de çalışır ve kontroller sırasında sorun tespit edilirse, rapor düzenlenir. Eğer sizin kodunuzdaki sorunlu bir nesne/değer Qt kütüphanesi tarafından erişiliyor ve sorun Qt içerisinde oluşuyorsa, bu durumda rapor alamayabilirsiniz. Sanitizer ile derlenmiş uygulama dosyalarını üretimde/dağıtımda kullanmanızı tasiye etmiyorum. Çünkü bu kütüphane yada uygulumalarda fazladan kontroller var ve bunlar uygulamayı yavaşlatabilir ve hafıza kullanımını biraz artırabilir.

valgrind kullanmak daha basit, çünkü derlenmiş uygulamayı çalıştırmak için kullanılıyor. Ancak raporlar yanıltıcı yada eksik olabilir. Çok görevli (multithread) uygulamalar tek görev üzerinden çalıştırıldığı için gerçekçi bir ortam oluşturamıyor. Bu yöntem çok yavaş çalışabilir. Valgrind, kodu inceleyerek değil çalıştırarak sorunları incelediği için sizin uygulamanızın dışında oluşan sorunları da tespit edebilir. Bu da büyük bir avantaj.

Kolay gelsin!

2 Beğeni