Seite 1 von 1

SIGSEGV nur unter Linux bei Datenübernahme aus einem Wizard

Verfasst: 23. November 2010 13:47
von barahir1983
Hallo Community,

ich habe ein relativ seltsames (und vielleicht auch betriebssystemspezifisches) Problem bei der Übernahme von Daten aus einem Wizard.
Ich definiere und fülle in diesem Wizard die Variable "header" folgendermaßen:

Code: Alles auswählen

public:
    explicit NAHeaderWizard(QWidget *parent = 0);
    ~NAHeaderWizard();
    QStringList header;
...

void NAHeaderWizard::accept()
{
    header.append("name:"+ui->nameE->text()+";");
    if (!ui->commentaryE->text().isEmpty())
        header.append("commentary:"+ui->commentaryE->text()+";");
    if (!ui->annotation1E->text().isEmpty())
        header.append("annotation:"+ui->annotation1E->text()+";");
    if (!ui->annotation2E->text().isEmpty())
        header.append("annotation:"+ui->annotation2E->text()+";");
     
   ... (hier folgen noch weitere Aufnahmen in die StringList)
    QDialog::accept();
}
Im Hauptprogramm wird der Inhalt der QStringList dann so eingefügt:

Code: Alles auswählen

void NaProg::on_actionHeader_Wizard_triggered()
{
    NAHeaderWizard wiz;
    if (activeMdiChild())
    {
        if (wiz.exec() == QDialog::Accepted)
        {
            QStringList::const_iterator it;
            for (it= wiz.header.constBegin(); it!= wiz.header.constEnd();++it)
            {
            activeMdiChild()->append(*it);
            }
        }
    }
    else
    {
        neu();
        if (wiz.exec() == QDialog::Accepted)
        {
            QStringList::const_iterator it;
            for (it= wiz.header.constBegin(); it!= wiz.header.constEnd();++it)
            {
            activeMdiChild()->append(*it);
            }
            activeMdiChild()->append("");
            on_actionClef_triggered();
        }
    }
}
Unter Windows und MacOS X funktioniert das alles auch und zum Schluss ist dann der Header in dem MDI-Fenster. Unter Linux passiert abwechselnd nichts, oder es gibt einen Segmentation fault. Ich habe leider keine Ahnung, was genau da schiefläuft und habe offen gestanden auch zu wenig Ahnung von den Unterschieden der Fensterverwaltung in den einzelnen Betriebssystemen, die möglicherweise dafür verantwortlich sind. Möglicherweise habe ich auch einfach nur Glück unter Windows und Mac OS X und das geht gar nicht so... :)

Wer hat Ideen zu diesem Problem? Über jedes "Augenöffnen" bin ich sehr dankbar, da auch Linux zu meinen Zielsystemen gehört und das natürlich blöd ist, dass es hier noch nicht funktioniert.

Liebe Grüße
barahir1983

Verfasst: 23. November 2010 14:51
von franzf
SIGSEGV ist immer böse und hat in fast 100% der Fälle mit fehlerhaftem Code auf deiner Seite zu tun. Auch wenn es nur auf einer Plattform zu Tage tritt.
Kannst du mal ein kompilierbares Minimalbeispiel posten, das den Laufzeitfehler aufweist? Ableitungen etc beibehlaten wie im Original. Mich würde auch noch interessieren, wie du in das "accept" gelangst.

Verfasst: 23. November 2010 23:06
von barahir1983
Hallo!

Leider bin ich gerade erst nach Hause gekommen und muss morgen recht früh raus, werde mich aber bald ans Minimalbeispiel setzen. Nur zu accept() ein Wort: Soweit ich weiß, wird das automatisch ausgelöst, sobald ich im Wizard auf "Fertigstellen" gehe. Also nicht über expliziten Aufruf meinerseits.

Mein Verdachtsmoment fällt immer wieder auf das activeMdiChild() der MdiArea. Ich hoffe, morgen ein Nichtfunktions-Beispiel geben zu können (mit einem einfachen Programm mit Textedit hat es nämlich funktioniert, auch unter Linux... (dieses Beispiel häng ich erstmal an, für die Funktionsweise an sich)

Liebe Grüße

barahir1983

Verfasst: 28. November 2010 19:15
von barahir1983
So, nun hier das versprochene Minimalbeispiel auf Basis des MDI-Beispiels von Qt...

Es führt genau zum "gewünschten" Fehlverhalten, nämlich einem schnöden "Segmentation fault" bei Ausführung von Test wizard. Um das Verhalten zu reproduzieren, muss man zunächst eine neue Datei erstellen, dann File->Test wizard ausführen; nach Click auf "Fertig" / "Done" wird der Segfault ausgelöst.

Tut mir leid wegen der längeren Verzögerung, dieses Wochenende hat mich mit mehr Arbeit als gedacht in Beschlag genommen...

Vielen Dank für alle Ideen/Vorschläge im Voraus
barahir1983

Verfasst: 28. November 2010 20:13
von franzf

Code: Alles auswählen

void MainWindow::testwizard()
{
    Wizard wiz;
    MdiChild *active = activeMdiChild();
    if(!active) return;
    if (wiz.exec() == QDialog::Accepted)
    {
        QStringList::const_iterator it;
        for (it= wiz.header.constBegin(); it!= wiz.header.constEnd();++it)
        {
        active->append(*it);
        }
    }
}
Dein Fenster hat direkt nach dem exec() noch keinen Focus, deshalb gibt es kein aktives MdiArea. Vor dem exec() zu prüfen macht also keinen Sinn.
Abänderung wie oben und es geht.

Der große Fehler bei deinem Code liegt in der ungeprüften Annahme, dass du nie und nirgends NULL-Pointer bekommst. Wenn du die Prüfung if(activeMdiChild()) in den exec()-if-Block ziehst, gibt es keinen SegFault - es wird nur auch kein Text eingefügt :P

Du castest auch recht gerne, ohne den return zu prüfen. Auch qobject_cast liefert bei nicht möglichem cast einen NULL-Pointer zurück. Den zu dereferenzieren löst einen SegFault aus. Geh mal deinen Code nach "_cast" durchsuchen und staune, wie viele potentielle Fehlerquellen du finden wirst :P

Verfasst: 28. November 2010 22:27
von barahir1983
Hallo und vielen Dank für die Antwort :)

Ich muss gestehen, die Stellen an denen qobject_cast so munter verwendet werden, habe ich noch aus dem QT-Beispiel, da hatte ich momentan noch nicht selbst dran gearbeitet... wie umgeht man denn die Gefahr "richtig"?
Wäre der Ansatz, statt jedes "if (activeMdiChild())" deinen Vorschlag

Code: Alles auswählen

MdiChild *active = activeMdiChild(); 
    if(!active) return; 
brauchbar oder sollte ich besser alles was mit der Überprüfung von MDI-Childs zusammenhängt noch mal neu schreiben?
Bitte um Entschuldigung, wenn meine Fragen etwas "doof" sind, ich bin erst seit einem halben Jahr aktiv an der Qt-Programmierung...

Habe gerade den Vorschlag eingearbeitet und siehe da, unter Linux klappts jetzt auch mit dem Einfügen :)

Liebe Grüße und vielen Dank
barahir1983

Verfasst: 29. November 2010 14:21
von franzf
barahir1983 hat geschrieben:Ich muss gestehen, die Stellen an denen qobject_cast so munter verwendet werden, habe ich noch aus dem QT-Beispiel, da hatte ich momentan noch nicht selbst dran gearbeitet... wie umgeht man denn die Gefahr "richtig"?
Indem man den return prüft.

Code: Alles auswählen

QWidget* w = new QTextEdit;
QTextEdit* t = qobject_cast<QTextEdit*>(w);
Q_ASSERT(t) // der geht gut
QLineEdit* e = qobject_cast<QLineEdit*>(w);
Q_ASSERT(e) // der geht schief - ein QTextEdit ist einfach kein QLineEdit, deshalb gibt der cast NULL zurück 
Q_ASSERT ist natürlich nur dann i.O., wenn es sich um nen Programmierfehler handelt. Ein normales "if(e)" tut es in den meisten Fällen.

Verfasst: 29. November 2010 14:31
von pfid
Auf die Lösung mit dem Nullpointer wärst du übrigens auch gekommen, wennn du dir einfach mal den Backtrace des Core-Files angeschaut hättest ;) (sofern ulimit das erzeugen erlaubt..)

Verfasst: 29. November 2010 19:25
von barahir1983
Ich werd mal sehen, dass ich das wasserfest abdichte :)

@pfid: Ich glaube nur bedingt, dass ich da drauf gekommen wäre, ich habe leider immer noch das Gefühl, bei Qt zu schwimmen, früher mit Delphi wars alles so "einfach" bzw. ich hab an sowas nie gedacht... Aber es ist gut zu wissen, so kümmere ich mich in Zukunft auch um die Null-pointer.

Liebe Grüße
barahir1983

Verfasst: 30. November 2010 09:14
von pfid
barahir1983 hat geschrieben:Ich werd mal sehen, dass ich das wasserfest abdichte :)

@pfid: Ich glaube nur bedingt, dass ich da drauf gekommen wäre, ich habe leider immer noch das Gefühl, bei Qt zu schwimmen, früher mit Delphi wars alles so "einfach" bzw. ich hab an sowas nie gedacht... Aber es ist gut zu wissen, so kümmere ich mich in Zukunft auch um die Null-pointer.

Liebe Grüße
barahir1983

Code: Alles auswählen

09:12:54 ~> gdb [binary] [core-file]

GNU gdb (GDB) 7.1-ubuntu
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
(gdb) bt [enter]
Dann suchst du nach der Zeile in der this=0x0 steht, und fixt den Bug ;)

Verfasst: 30. November 2010 15:57
von barahir1983
Danke, wieder was neues gelernt :)