Segmentation fault nur auf Raspberry Pi, sonst nicht



  • Hallo,
    ich habe ein C++ Projekt, welches unter Linux und Windows läuft. Das Projekt ist noch nicht fertig, aber es läuft stabil und fehlerfrei unter Linux (Ubuntu und Kubuntu getestet) und Windows (XP SP3 und 7 getestet) mit 32 und mit 64 Bit Systemen.
    Seit kurzem habe ich einen Raspberry Pi Modell B und wollte nun testen, ob das Projekt auch auf ihm funktioniert. Ich habe die nötigen Bibliotheken installiert und das Projekt ging ohne Fehlermeldung mit g++ kompilieren.
    Über die Konsole erhalte ich jedoch beim Starten einen Speicherzugriffsfehler gemeldet und das Programm wird beendet.
    gdb spuckt mir folgendes dazu aus:

    [Thread debugging using libthread_db enabled]
    Using host libthread_db library "/lib/arm-linux-gnueabihf/libthread_db.so.1".
    [New Thread 0xb5f90420 (LWP 10469)]
    [New Thread 0xb5790420 (LWP 10470)]
    
    Program received signal SIGSEGV, Segmentation fault.
    0xb6b91e68 in ?? () from /lib/arm-linux-gnueabihf/libc.so.6
    (gdb) info s
    #0  0xb6b91e68 in ?? () from /lib/arm-linux-gnueabihf/libc.so.6
    #1  0x00000000 in ?? ()
    

    Die Funktion "??" klingt jetzt eher ziemlich mystisch. 😕

    Ich habe jetzt gar keine Ahnung, wo der Fehler bzw. das Problem liegen könnte. Weiß jemand ob und wie ich hier weiter kommen könnte bzw. woran das liegen könnte?
    Das Projekt verwendet OpenGL für 2D Grafikdarstellung, falls das vielleicht weiter hilft. (Fehler beim Laden der Texturen o.ä.) Aber da müsste ja eigentlich eine OpenGL-Bibliothek o.ä. meckern, wenn es dort Probleme geben würde.

    P.S. Bitte entschuldigt, wenn irgendwo im Text einzelne Buchstaben fehlen sollten. Ich schreibe gerade vom Raspberry Pi aus, der gleichzeitig noch andere Dinge im Hintergrund macht und deshalb ab und zu in paar Buchstaben verschluckt. Ich habe versucht alle fehlenden Buchstaben wieder zu ergänzen. 😉

    Viele Grüße,

    Little Programmer


  • Mod

    Das ?? bedeutet, dass die nötigen Debugsymbole nicht zur Verfügung stehen, um genauere Angaben zu machen.

    Allgemein klingt das nach einem Problem, das auch auf den anderen Plattformen auftreten sollte, aber dort vielleicht nicht auffällt (weil undefiniertes Verhalten). Wenn das C++ ist, dann schalte mal den Debugmodus der STL an. Programme wie valgrind finden auch viele Fehler, die im Programmtestdurchlauf nicht direkt auffallen.



  • Naja, abgesehen von x86 scheinst du ja sonst nichts anderes getestet zu haben. Nur weil ein Stück Code auf einem gutmütigen Allesfresser-x86 läuft, ist es noch lange nicht korrekt. 😉

    Ein paar Tipps in Blaue:

    - Alignment: im Gegensatz zu einem x86er haut ein ARM (sowie praktisch alle anderen RISC-Prozessoren) dir einen unaligned Speicherzugriff um die Ohren, anstatt ihn hinter deinem Rücken (mit Performanceverlust, selbstverständlich) irgendwie gerade zu biegen. Suche dein Programm nach Pointercasts ab.

    - Dataraces: benutzt du in deinem Programm Threads? Benutze mal einen Race-Detektor. Das kannst du ruhig auf deinem x86-Linux machen. Wenn du einen aktuellen GCC oder Clang hast, kompiliere und linke dein Program mit -fsanitize=thread. Führe das resultierende Binary aus und benutze es dann ganz normal.

    Auch Valgrind hat vergleichbare Tools (Helgrind, drd, …). Mit Clang kannst du auch -fsanitize=undefined probieren.



  • Danke für die Antworten. 👍
    Ich war am Wochenende leider unterwegs und konnte deshalb weder am dem Problem tüfteln, noch hier antworten.
    @free-is-write, nein, ich verwende keine Threads, die Threads stammen aus der SDL-Bibliothek.

    Ich habe valgrind mal ausprobiert und das gruselige ist, der überwiegende Teil der Meldungen kommt von den Interna der SDL-Bibliothek. 😮

    Die Fehler aus meinem Programm habe ich weitestgehend entfernt. Aber an zwei Stellen mit gleicher Fehlermeldung finde ich den Fehler nicht:

    ==3020== Mismatched free() / delete / delete []
    ==3020==    at 0x4025851: operator delete(void*) (vg_replace_malloc.c:387)
    ==3020==    by 0x807BEA8: GLNet::setPacketSize(int) (GLNet.cpp:2551)
    ==3020==    by 0x804C61C: main (main.cpp:59)
    ==3020==  Address 0x62047a0 is 0 bytes inside a block of size 1,024 alloc'd
    ==3020==    at 0x402632E: operator new[](unsigned int) (vg_replace_malloc.c:299)
    ==3020==    by 0x8072734: GLNet::GLNet(GLTime*) (GLNet.cpp:74)
    ==3020==    by 0x804C5E7: main (main.cpp:58)
    

    Von unten nach oben die entsprechenden Quelltextzeilen:

    main.cpp 58 (erstellt ein Netzwerkobjekt, über welches die Netzwerkkommunikation abgewickelt wird)

    GLNet Netzwerk(&Zeit);
    

    GLNet.cpp 74 (reserviert im Konstruktor des Netzwerkobjekts einen Puffer mit voreingestellter Größe (1024 bit) über den Netzwerknachrichten ausgelesen werden.)

    Puffer = new char[Plaenge];
    

    Aus dieser Erstellung resultiert dann die oben genannte Meldung:

    Address 0x62047a0 is 0 bytes inside a block of size 1,024 alloc'd
    

    Die erste Fehlermeldung erfolgt danach eine Zeile tiefer in der main.cpp. Dort ändere ich die Größe des Puffers manuell.

    main.cpp 59 (Puffergröße ändern)

    Netzwerk.setPacketSize(128);
    

    GLNet.cpp Zeile 2551 ist hier die Zeile 9. Das neu Erstellen des Puffers danach resultiert in keinem Fehler.

    void GLNet::setPacketSize(int laenge)
    {
    	Plaenge = laenge;
    
    	//Größe des UDP-Pakets anpassen
    	SDLNet_ResizePacket(Packet, Plaenge);
    
    	//Größe des Aulesepuffers anpassen
    	delete Puffer;
    	Puffer = new char[Plaenge];
    }
    

    Und hier habe ich noch einmal die gleiche Fehlermeldung für eine andere Stelle erhalten:

    ==3020== Mismatched free() / delete / delete []
    ==3020==    at 0x4025851: operator delete(void*) (vg_replace_malloc.c:387)
    ==3020==    by 0x806B695: GLBitFont::draw() (GLBitFont.cpp:416)
    ==3020==    by 0x80D6BD8: Menu::renderMenu() (Menu.cpp:10610)
    ==3020==    by 0x804E9C1: main (main.cpp:285)
    ==3020==  Address 0x62479a8 is 0 bytes inside a block of size 28 alloc'd
    ==3020==    at 0x402632E: operator new[](unsigned int) (vg_replace_malloc.c:299)
    ==3020==    by 0x806ADE9: GLBitFont::draw() (GLBitFont.cpp:334)
    ==3020==    by 0x80D6BD8: Menu::renderMenu() (Menu.cpp:10610)
    ==3020==    by 0x804E9C1: main (main.cpp:285)
    

    Hier wird der Speicher in der gleichen Funktion erstellt und gelöscht, deshalb hier nur die entscheidenden Ausschnitte aus der draw()-Funktion des GLBitfont-Objekts um das es geht. Die erste Zeile ist Zeile 334 aus der Fehlermeldung und die letzte Zeile 416:

    char* CText = new char [Text.length()+1];
    strcpy (CText, Text.c_str());
    
    //diverse Grafikroutinen, in denen CText nur byteweise ausgelesen und nicht verändert wird
    
    //Puffer löschen
    delete CText;
    

    Da beide Fehlermeldungen inhaltlich identisch sind und ich keinen Fehler sehe, nehme ich mal an, dass ich grundsätzlich irgendwas falsch gemacht habe?

    Könnt ihr mir vielleicht sagen wo die Fehler liegen?

    Viele Grüße,

    Little Programmer


  • Mod

    Nichts für ungut, aber das ist irgendwie selber Schuld. Wenn man selber manuell Ressourcen verwaltet, dann führt dies eben zu folgendem:
    -Fehlern
    -schwer zu findende Fehlern
    -unmöglich zu findenden Fehlern

    Wieso nutzt du nicht vector, string, unique_ptr & Co? Was du gezeigt hast, ließe sich durch diese perfekt ersetzen und du hättest gar nicht mehr die Möglichkeit, solche Fehler zu erzeugen. Selbst wenn du sehr spezielle Anforderungen an deine Ressourcen hast, zum Beispiel weil du irgendwelche Objekte einer C-Bibliothek halten musst, dann programmierst du dir eine eigene Halterklase dafür, die RAII korrekt implementiert. In allen Fällen ist der resultierende Code kürzer, einfacher, übersichtlicher, robuster und von ganz alleine ressourcenleckfrei. Zudem auch noch im Falle eines Falles viel einfacher zu Debuggen. Wie ich schon oben gesagt gesagt habe, hättest du einfach den Debugmodus der STL anwerfen können, um Fehler in der Programmlogik zu finden (z.B. Bereichsüberschreitungen). Aber du hast die STL ja umgangen 😞 . Bei einem eigenen Ressourcenhandler hättest du ebenfalls leicht eigene Debugoptionen einbauen können.

    Ich habe valgrind mal ausprobiert und das gruselige ist, der überwiegende Teil der Meldungen kommt von den Interna der SDL-Bibliothek.

    Auch dies liegt wahrscheinlich da dran, dass dein Code Ressourcen leckt.

    Du wirst vermutlich niemanden finden, der dir freiwillig hilft, solch ein selbstgemachtes Chaos zu debuggen. Wie schon gesagt, ist das sehr schwer. Ich würde alles nochmal umschreiben, aber dann vernünftig mit RAII. Und vorher mal ein Buch zu C++-Stil lesen.



  • Der konkrete Fehler liegt in diesem Fall darin, dass du Speicher mit new[] beschaffst und ihn mit delete freigibst. new[] erfordert delete[].

    Benutzt du OpenGL eigentlich ausschließlich über SDL oder machst du auch direkte Calls von OpenGL-Funktionalität? Als erstes solltest du dich aber in der Tat um die von Valgrind ausgespuckten Fehler kümmern.



  • Vorallem wird der Raspberry Pi weniger Speicher besityen als ein Desktoprechner. Checkst du auch, ob du angeforderten Speicher auf tatseachlich bekommst?


  • Mod

    knivil schrieb:

    Vorallem wird der Raspberry Pi weniger Speicher besityen als ein Desktoprechner. Checkst du auch, ob du angeforderten Speicher auf tatseachlich bekommst?

    Immerhin das wird sich bei einem C++-new in einer Exception äußern, so dass dies hier wohl kaum der Fehler sein kann.



  • @ meh_, danke für den Hinweis mit dem delete[]. Das war mir neu, dachte bisher immer es gäbe nur ein delete für alles.

    @ all, da mir valgrind jede Menge Fehler in SDL und OpenGL liefert, teilweise sogar nicht mal durch Funktionsaufrufe von mir ausgelöst, sondern einfach so z.B.:

    ==3020== 400 bytes in 1 blocks are definitely lost in loss record 169 of 196
    ==3020==    at 0x4025F20: malloc (vg_replace_malloc.c:236)
    ==3020==    by 0x5C7993D: XGetVisualInfo (in /usr/lib/libX11.so.6.3.0)
    ==3020==    by 0x41336A9: glXChooseVisual (in /usr/lib/nvidia-current/libGL.so.195.36.24)
    

    Habe ich jetzt einfach mal das einfachst-mögliche SDL-Programm ohne jegliche Zusatzbibliothek genommen und durch valgrind laufen lassen:

    #include "SDL/SDL.h"
    int main( int argc, char* args[] )
    {
    	//Start SDL
    	SDL_Init( SDL_INIT_EVERYTHING );
    	//Quit SDL
    	SDL_Quit();
    	return 0;
    }
    

    Auf:

    valgrind --leak-check=full --track-origins=yes ./Valgrindtest
    

    (Valgrindtest ist hier der Programmname) folgt:

    ==2747== Memcheck, a memory error detector
    ==2747== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
    ==2747== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
    ==2747== Command: ./Valgrindtest
    ==2747== 
    ==2747== 
    ==2747== HEAP SUMMARY:
    ==2747==     in use at exit: 90,949 bytes in 1,262 blocks
    ==2747==   total heap usage: 14,530 allocs, 13,268 frees, 2,133,091 bytes allocated
    ==2747== 
    ==2747== 26 bytes in 2 blocks are definitely lost in loss record 35 of 153
    ==2747==    at 0x4025F20: malloc (vg_replace_malloc.c:236)
    ==2747==    by 0x4526384: ??? (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x4525221: _XimEncodeLocalICAttr (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x4526615: _XimSetICValueData (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x452DC11: _XimLocalCreateIC (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x4508AD8: XCreateIC (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x40855D4: ??? (in /usr/lib/libSDL-1.2.so.0.11.3)
    ==2747==    by 0x40867D3: ??? (in /usr/lib/libSDL-1.2.so.0.11.3)
    ==2747==    by 0x4074AAA: SDL_VideoInit (in /usr/lib/libSDL-1.2.so.0.11.3)
    ==2747==    by 0x404A8B3: SDL_InitSubSystem (in /usr/lib/libSDL-1.2.so.0.11.3)
    ==2747==    by 0x404A916: SDL_Init (in /usr/lib/libSDL-1.2.so.0.11.3)
    ==2747==    by 0x80485D8: main (main.cpp:5)
    ==2747== 
    ==2747== 112 (8 direct, 104 indirect) bytes in 1 blocks are definitely lost in loss record 120 of 153
    ==2747==    at 0x4026016: realloc (vg_replace_malloc.c:525)
    ==2747==    by 0x450EAFE: ??? (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x450F82D: ??? (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x4511011: ??? (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x4511774: _XlcCreateLC (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x4531C0A: _XlcDefaultLoader (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x4519763: _XOpenLC (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x45198D2: _XlcCurrentLC (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x4519D71: XSetLocaleModifiers (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x407C28D: ??? (in /usr/lib/libSDL-1.2.so.0.11.3)
    ==2747==    by 0x40858FF: ??? (in /usr/lib/libSDL-1.2.so.0.11.3)
    ==2747==    by 0x40867D3: ??? (in /usr/lib/libSDL-1.2.so.0.11.3)
    ==2747== 
    ==2747== 112 (8 direct, 104 indirect) bytes in 1 blocks are definitely lost in loss record 121 of 153
    ==2747==    at 0x4026016: realloc (vg_replace_malloc.c:525)
    ==2747==    by 0x450EAFE: ??? (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x450F82D: ??? (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x4511011: ??? (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x4511774: _XlcCreateLC (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x4531C0A: _XlcDefaultLoader (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x4519763: _XOpenLC (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x451988D: _XrmInitParseInfo (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x44FF920: ??? (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x4501977: XrmGetStringDatabase (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x44DE509: ??? (in /usr/lib/libX11.so.6.3.0)
    ==2747==    by 0x44DE6FE: XGetDefault (in /usr/lib/libX11.so.6.3.0)
    ==2747== 
    ==2747== LEAK SUMMARY:
    ==2747==    definitely lost: 42 bytes in 4 blocks
    ==2747==    indirectly lost: 208 bytes in 8 blocks
    ==2747==      possibly lost: 0 bytes in 0 blocks
    ==2747==    still reachable: 90,699 bytes in 1,250 blocks
    ==2747==         suppressed: 0 bytes in 0 blocks
    ==2747== Reachable blocks (those to which a pointer was found) are not shown.
    ==2747== To see them, rerun with: --leak-check=full --show-reachable=yes
    ==2747== 
    ==2747== For counts of detected and suppressed errors, rerun with: -v
    ==2747== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 79 from 8)
    

    Also entweder ist valgrind etwas "überempfindlich" und findet an manchen Stellen Fehler wo gar keine sind, oder die SDL-Bibliothek hat tatsächlich Fehler. An meinem Testprogramm oben kanns wohl kaum liegen. 😉


  • Mod

    Ja, das ist ein bekanntes Verhalten der Xlib.


Anmelden zum Antworten