Ergebnis 1 bis 4 von 4

C++ Core Dump, wie herausfinden?

  1. #1 Zitieren
    Legende Avatar von Testgrave
    Registriert seit
    Jan 2007
    Ort
    Florida
    Beiträge
    7.451
    Hallo euch allen,
    vorweg, ich wohne jetzt schon seit ueber 3 jahren in den USA und spreche kaum mehr Deutsch, also bitte habt Ruecksicht. Vorallen mein Informatikwissen ist hauptsaechlich Englisch

    Ich bin gerade in einem Algorithmen und Datenstrukturen Kurs und alle zwei Wochen muss ich dafuer ein Projekt einreichen, dass dann auf einem Linux server via g++ gegen Testfaelle geprueft wird.

    Jetzt hab ich gerade eine relativ schlechte Note bekommen mit der Begruendung, dass alle Testfaelle okay waren, aber in fuenf Faellen es einen "Core Dump" gab weil ich das keyword delete zu oft verwendet habe.

    Jetzt stellt sich mir aber die Frage, wie finde ich das raus? Mein Program lief in der vorgegebenen Zeit und gab den richtigen Output und wurde auch ohne Probleme compiliert. Irgendwie konnte der TA das ja auch herausfinden, ansonsten haette er mir ja direkt 100/100 Punkten gegeben

    Vielen Dank im voraus


    mfg Test-Grave
    [Bild: Newknightsig.gif]
    Der schrickestu gerñ kain uechtñ nÿmer gelerñ
    Testgrave ist offline

  2. #2 Zitieren

    Batmanistrator
    Avatar von Thoronador
    Registriert seit
    Jul 2005
    Ort
    Morrowind, Vvardenfell-Distrikt
    Beiträge
    20.426
    Die einfachste Möglichkeit: Lass dir die Testfälle geben und führe dein Programm mit diesen Testfällen aus.

    Da das vermutlich keine Option ist: Vermeide new und delete, wenn möglich.
    Bei manueller Speicherverwaltung mit new und delete besteht immer das Problem, dass man zuvor mit new allokierten Speicher auch wieder mit delete freigegeben werden muss - und zwar genau ein einziges Mal. Vergisst man das Freigeben mit delete, dann führt das zu Speicherlecks ("memory leaks"). Gibt man den Speicher mehr als einmal frei, dann bekommt man einen Speicherzugriffsfehler ("segementation fault" unter Linux). Vermutlich ist das in deinem Fall auch der Grund für den Core Dump.

    Mal ein Beispiel:
    Code:
    #include <iostream>
    
    int main(void)
    {
      int * ptr = new int;
      *ptr = 1;
      std::cout << "Value is " << *ptr << std::endl;
      
      while(true)
      {
        std::clog << "Deleting ptr ..." << std::endl;
        delete ptr;
      }
      return 0;
    }
    Hier wird Speicher für eine Variable vom Typ int allokiert. Danach wird ein Wert zugewiesen und dieser Wert ausgegeben. Dann kommt der interessante Teil: In einer theoretisch endlos laufenden Schleife (zumindest ist die Bedingung für die while-Schleife immer wahr) wird versucht, den Speicher freizugeben - immer wieder. Man hat nach dem Kompilieren und Ausführen des Programmes in etwa folgende Ausgabe:
    Code:
    Value is 1
    Deleting ptr ...
    Deleting ptr ...
    *** glibc detected *** ./test.run: double free or corruption (fasttop): 0x085d6008 ***
    ======= Backtrace: =========
    /lib/i386-linux-gnu/i686/cmov/libc.so.6(+0x6ff41)[0xb7564f41]
    /lib/i386-linux-gnu/i686/cmov/libc.so.6(+0x717a8)[0xb75667a8]
    /lib/i386-linux-gnu/i686/cmov/libc.so.6(cfree+0x6d)[0xb75698ed]
    /usr/lib/i386-linux-gnu/libstdc++.so.6(_ZdlPv+0x1f)[0xb76ee4bf]
    /lib/i386-linux-gnu/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xb750be46]
    ./test.run[0x8048721]
    ======= Memory map: ========
    08048000-08049000 r-xp 00000000 08:06 2649761    /home/dirk/test.run
    08049000-0804a000 rw-p 00000000 08:06 2649761    /home/dirk/test.run
    085d6000-085f7000 rw-p 00000000 00:00 0          [heap]
    b7300000-b7321000 rw-p 00000000 00:00 0 
    b7321000-b7400000 ---p 00000000 00:00 0 
    b74f3000-b74f5000 rw-p 00000000 00:00 0 
    b74f5000-b7656000 r-xp 00000000 08:06 1322885    /lib/i386-linux-gnu/i686/cmov/libc-2.13.so
    b7656000-b7657000 ---p 00161000 08:06 1322885    /lib/i386-linux-gnu/i686/cmov/libc-2.13.so
    b7657000-b7659000 r--p 00161000 08:06 1322885    /lib/i386-linux-gnu/i686/cmov/libc-2.13.so
    b7659000-b765a000 rw-p 00163000 08:06 1322885    /lib/i386-linux-gnu/i686/cmov/libc-2.13.so
    b765a000-b765d000 rw-p 00000000 00:00 0 
    b765d000-b7679000 r-xp 00000000 08:06 1323626    /lib/i386-linux-gnu/libgcc_s.so.1
    b7679000-b767a000 rw-p 0001b000 08:06 1323626    /lib/i386-linux-gnu/libgcc_s.so.1
    b767a000-b767b000 rw-p 00000000 00:00 0 
    b767b000-b769f000 r-xp 00000000 08:06 1828253    /lib/i386-linux-gnu/i686/cmov/libm-2.13.so
    b769f000-b76a0000 r--p 00023000 08:06 1828253    /lib/i386-linux-gnu/i686/cmov/libm-2.13.so
    b76a0000-b76a1000 rw-p 00024000 08:06 1828253    /lib/i386-linux-gnu/i686/cmov/libm-2.13.so
    b76a1000-b7781000 r-xp 00000000 08:06 1196075    /usr/lib/i386-linux-gnu/libstdc++.so.6.0.17
    b7781000-b7785000 r--p 000e0000 08:06 1196075    /usr/lib/i386-linux-gnu/libstdc++.so.6.0.17
    b7785000-b7786000 rw-p 000e4000 08:06 1196075    /usr/lib/i386-linux-gnu/libstdc++.so.6.0.17
    b7786000-b778d000 rw-p 00000000 00:00 0 
    b77aa000-b77ad000 rw-p 00000000 00:00 0 
    b77ad000-b77ae000 r-xp 00000000 00:00 0          [vdso]
    b77ae000-b77ca000 r-xp 00000000 08:06 995924     /lib/i386-linux-gnu/ld-2.13.so
    b77ca000-b77cb000 r--p 0001b000 08:06 995924     /lib/i386-linux-gnu/ld-2.13.so
    b77cb000-b77cc000 rw-p 0001c000 08:06 995924     /lib/i386-linux-gnu/ld-2.13.so
    bff94000-bffb5000 rw-p 00000000 00:00 0          [stack]
    Abgebrochen
    Das heißt: Die "Endlosschleife" läuft genau zwei Mal durch. Beim zweiten Mal gibt es einen Fehler, da der Speicherbereich, auf welchen der Pointer ptr zeigt, schon freigegeben wurde. Ein zweites Mal kann man den Speicher nicht freigeben, daher steigt das Programm aus. So etwas ähnliches wird vermutlich auch bei deinem Programm passiert sein.

    Eine andere Möglichkeit ist, dass Speicher zwar nur einmal freigegeben wurde (bis dahin ist es OK), aber danach versucht wird, auf das freigegebene Objekt zuzugreifen. Auch das verursacht in der Regel eine "segmentation fault" und Programmabsturz.

    Die Kunst ist nun, diese Fälle zu umgehen, und das auch bei komplexerem Code als dem obigen Beispiel. In modernem C++ (d.h. ab C++11) nutzt man daher in der Regel "Smart Pointers" wie std::unique_ptr und std::shared_ptr anstatt direkt mit new und delete zu hantieren. Das hat den Vorteil, dass man sich nicht mehr explizit um das Speichermanagement kümmern muss und der zugewiesene Speicher auch wieder von diesen Klassen freigegeben wird, sobald der Gültigkeitsbereich ("scope") des smart pointer verlassen wird - egal ob durch regulären Programmfluss oder durch eine Exception. Bei "nackten" Pointern, die mit new initialisiert wurden, ist das nicht der Fall; dort muss immer der Programmierer explizit dafür sorgen.
    Thoronador ist offline

  3. #3 Zitieren
    Legende Avatar von Testgrave
    Registriert seit
    Jan 2007
    Ort
    Florida
    Beiträge
    7.451
    Okay, das hilft schonmal sehr!
    Es war definitiv ein segmentation fault der zum Core Dump gefuehrt hat.

    Die Testfaelle habe ich aber nachdem ich die Note bekommen habe, auch gekriegt, aber wenn ich das Programm ueber g++ compiliere und dann im terminal ausfuehre (mit den Testfaellen wo ich keine Punkte bekommen habe), dann krieg ich trotzdem keinen error und alles laeuft gut und unter der vorgegebenen Zeit.

    Sprich:
    Code:
    testgrave@testgrave-VirtualBox:~/Documents/Testing$ g++ main.cpp -o exe
    testgrave@testgrave-VirtualBox:~/Documents/Testing$ time ./exe >out <in0
    
    real	0m0.033s
    user	0m0.000s
    sys	0m0.000s
    testgrave@testgrave-VirtualBox:~/Documents/Testing$ diff -bB out out0
    testgrave@testgrave-VirtualBox:~/Documents/Testing$ time ./exe >out <in3
    
    real	0m0.826s
    user	0m0.740s
    sys	0m0.016s
    testgrave@testgrave-VirtualBox:~/Documents/Testing$ diff -bB out out3
    Vorallem im zweiten Input (in3), weis ich dass es fuer den TA ca. 1 millionen segmentation faults gab, aber wenn ich es in Linux (Ubuntu) ausfuehre, klappt alles gut. Kein Error und der Output ist auch richtig.


    Zitat Zitat von Thoronador Beitrag anzeigen
    Die Kunst ist nun, diese Fälle zu umgehen, und das auch bei komplexerem Code als dem obigen Beispiel. In modernem C++ (d.h. ab C++11) nutzt man daher in der Regel "Smart Pointers" wie std::unique_ptr und std::shared_ptr anstatt direkt mit new und delete zu hantieren. Das hat den Vorteil, dass man sich nicht mehr explizit um das Speichermanagement kümmern muss und der zugewiesene Speicher auch wieder von diesen Klassen freigegeben wird, sobald der Gültigkeitsbereich ("scope") des smart pointer verlassen wird - egal ob durch regulären Programmfluss oder durch eine Exception. Bei "nackten" Pointern, die mit new initialisiert wurden, ist das nicht der Fall; dort muss immer der Programmierer explizit dafür sorgen.
    Das werde ich mir aufjedenfall mal anschauen. Wir duerfen C++11 benutzen, also das waere aufjedenfall eine Moeglichkeit. Laeuft das dann wie Garbage Collection in Java? Oder ist das wieder ein anderes Model fuers Speichermanagement?


    mfg Test-Grave
    [Bild: Newknightsig.gif]
    Der schrickestu gerñ kain uechtñ nÿmer gelerñ
    Testgrave ist offline Geändert von Testgrave (20.02.2016 um 23:53 Uhr)

  4. #4 Zitieren

    Batmanistrator
    Avatar von Thoronador
    Registriert seit
    Jul 2005
    Ort
    Morrowind, Vvardenfell-Distrikt
    Beiträge
    20.426
    Zitat Zitat von Testgrave Beitrag anzeigen
    Vorallem im zweiten Input (in3), weis ich dass es fuer den TA ca. 1 millionen segmentation faults gab, aber wenn ich es in Linux (Ubuntu) ausfuehre, klappt alles gut. Kein Error und der Output ist auch richtig.
    Das ist eigenartig. Wobei "ca. 1 Million segmentation faults" eigentlich nicht möglich ist, ein Programm steigt normalerweise nach der ersten aus.
    Weißt du zufällig, auf welchem System das Programm zwecks Benotung getestet wird und welcher C++-Compiler in welcher Version dort genutzt wird? Vielleicht könntest du das System dann in einer virtuellen Maschine nachbilden (gleiches Betriebssystem, gleiche Compilerversion) und es dort mal testen.

    Falls das nicht möglich ist, könntest du hier auch mal den relevanten Codeabschnitt zeigen. Vielleicht ist daran etwas auffällig.

    Zitat Zitat von Testgrave Beitrag anzeigen
    Das werde ich mir aufjedenfall mal anschauen. Wir duerfen C++11 benutzen, also das waere aufjedenfall eine Moeglichkeit. Laeuft das dann wie Garbage Collection in Java? Oder ist das wieder ein anderes Model fuers Speichermanagement?
    Nein, das läuft nicht ganz so wie Garbage Collection in Java. std::shared_ptr geht aber in die Richtung: Intern wird in der Struktur zusätzlich zum Pointer ein Referenzzähler genutzt, der zählt, wie viele std::shared_ptr-Objekte noch auf diesen Pointer verweisen. Fällt dieser Zähler auf 0, wird der Zeiger freigegeben. std::unique_ptr gibt den Speicher des zugehörigen Objektes frei, sobald der Gültigkeitsbereich des std::unique_ptr-Objektes verlassen wird:
    std::unique_ptr is a smart pointer that retains sole ownership of an object through a pointer and destroys that object when the unique_ptr goes out of scope. No two unique_ptr instances can manage the same object.

    The object is destroyed and its memory deallocated when either of the following happens:
    • unique_ptr managing the object is destroyed
    • unique_ptr managing the object is assigned another pointer via operator= or reset().
    Grundidee dahinter ist, dass man anstelle von
    Code:
    Foo * p1 = new Foo;
    einfach
    Code:
    std::unique_ptr<Foo> p1(new Foo);
    verwendet. (Foo sei hier der Name eines Typs bzw. einer Klasse.) Damit besteht nicht die Gefahr, dass man die zugehörige delete-Anweisung zur Speicherfreigabe vergisst, denn darum kümmert sich std::unique_ptr. Zweimal freigeben kann man das Objekt im Pointer dann auch nicht. (Es sei denn, man nutzt std::unique_ptr::release() und verwaltet den Pointer wieder selbst, aber das ist eigentlich nicht der Sinn dieser Klasse.)

    Von den beiden Klassen std::unique_ptr und std::shared_ptr sollte man in den meisten Fällen std::unique_ptr nehmen. Es sei denn, man will diesen Pointer an verschiedene Funktionen/Objekte weiterreichen, die auch darauf zugreifen können; dann ist std::shared_ptr angebracht. Vermutlich ist es gut, wenn du dir mal die eher schon verlinkte Seite zu den beiden Pointertypen auf cppreference.com durchliest und dir ein paar der Beispiele anschaust, um die Grundidee zu verstehen.


    Aber am sichersten umgeht man segmentation faults durch falschen Umgang mit Pointern, indem man Pointer vermeidet. Codes wie
    Code:
    int myFunction()
    {
      Foo * ptr = new Foo; //allocate memory + create object of class Foo
      ...
      //do something with ptr
      ...
      delete ptr; //destroy object + free memory
      ptr = nullptr; //to avoid dangling pointer
      ...
      return 1;
    }
    bergen immer die Gefahr, dass zwischen new und delete irgendetwas eintritt, was verhindert, dass man beim delete ankommt. Das kann beispielsweise eine Exception sein oder (wenn man innerhalb einer Funktion ist) eine return-Anweisung ohne vorheriges delete.
    Stattdessen könnte man den Code mit smart pointern umschreiben:
    Code:
    int myFunction()
    {
      //allocate memory + create object of class Foo, manage by unique_ptr
      std::unique_ptr<Foo> ptr(new Foo);
      ...
      //do something with ptr
      ...
      //no delete required, std::unique_ptr will do that
      return 1;
    }
    Noch einfacher geht's natürlich ohne Pointer, wenn man das Objekt außerhalb der Funktion ohnehin nicht nutzt:
    Code:
    int myFunction()
    {
      Foo f1;
      ...
      //do something with f1
      ...
      return 1;
    }
    Damit hat man gar keinen Pointer mehr und das Objekt f1 wird zerstört, sobald die Funktion verlassen wird.
    Thoronador ist offline

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •