*** glibc detected *** double free or corruption



  • Hi everyone

    Dieser Fehler hat mich letztens nach längerer Laufzeit meines Programms begrüsst. Das Programm liest mit mehreren Threads von mehreren Schnittstellen gleichzeitig aus. Weil mich die LibSerial in diese Angelegenheit enttäuscht hat, habe ich eine eigene Klasse erstellt:

    SerialPort.h:

    #ifndef SIMPLESERIALPORT_H_
    #define SIMPLESERIALPORT_H_
    
    #include "BaudRate.h"
    #include "CharacterSize.h"
    #include "Parity.h"
    #include "StopBits.h"
    #include "FlowControl.h"
    #include "Exception/CloseException.h"
    #include "Exception/ControlException.h"
    #include "Exception/NotOpenException.h"
    #include "Exception/OpenException.h"
    #include "Exception/ReadException.h"
    #include "Exception/TimeoutException.h"
    #include "Exception/WriteException.h"
    #include <boost/date_time/posix_time/posix_time_types.hpp>
    #include <string>
    #include <map>
    
    namespace simpleserial {
    class SerialPort {
    public:
        SerialPort();
        SerialPort(const std::string &, const BaudRate, const CharacterSize,
                const FlowControl, const Parity, const StopBits);
        virtual ~SerialPort();
    
        void open() throw(OpenException);
        void open(const std::string &, const BaudRate, const CharacterSize,
                const FlowControl, const Parity, const StopBits)
                throw(CloseException, OpenException);
        void close() throw(CloseException);
    
        void setDeviceLink(const std::string &);
        void setBaudRate(const BaudRate);
        void setCharacterSize(const CharacterSize);
        void setFlowControl(const FlowControl);
        void setMaxCharacterDelay(const boost::posix_time::milliseconds &);
        void setParity(const Parity);
        void setStopBits(const StopBits);
        void applySettings() const throw(ControlException, NotOpenException);
    
        const std::string &getDeviceLink() const;
        BaudRate getBaudRate() const;
        CharacterSize getCharacterSize() const;
        FlowControl getFlowControl() const;
        const boost::posix_time::milliseconds getMaxCharacterDelay() const;
        Parity getParity() const;
        StopBits getStopBits() const;
        bool isOpen() const;
    
        int write(const std::string &) const throw(NotOpenException,
                WriteException);
        void read(std::string &, const int) const throw(NotOpenException,
                ReadException, TimeoutException);
        int readIfAvailable(std::string &, const int) const throw(NotOpenException,
                ReadException);
    private:
        int fd;
        const static int CLOSED_FD;
        std::string deviceLink;
        BaudRate baudRate;
        CharacterSize characterSize;
        Parity parity;
        StopBits stopBits;
        FlowControl flowControl;
        boost::posix_time::milliseconds maxCharacterDelay;
        const static boost::posix_time::milliseconds BLOCKING_CHARACTER_DELAY;
    
        void applyGenerals() const throw(ControlException);
        void applyBaudRate() const throw(ControlException);
        void applyCharacterSize() const throw(ControlException);
        void applyFlowControl() const throw(ControlException);
        void applyParity() const throw(ControlException);
        void applyStopBits() const throw(ControlException);
        void applyMaxCharacterDelay() const throw(ControlException);
    
        const static std::map<BaudRate, int> simpleSerialBaudRateToPosix;
        const static std::map<CharacterSize, int> simpleSerialCharacterSizeToPosix;
    };
    }
    
    #endif /*SIMPLESERIALPORT_H_*/
    

    SerialPort.cpp:

    #include <simpleSerial/SerialPort.h>
    #include <boost/assign/list_of.hpp>
    #include <fcntl.h>
    #include <termios.h>
    
    using namespace boost::assign;
    using namespace boost::posix_time;
    using namespace std;
    
    namespace simpleserial {
    const int SerialPort::CLOSED_FD = -1;
    const milliseconds SerialPort::BLOCKING_CHARACTER_DELAY(0);
    const map<CharacterSize, int> SerialPort::simpleSerialCharacterSizeToPosix =
            map_list_of(CHAR_SIZE_5, CS5)(CHAR_SIZE_6, CS6)(CHAR_SIZE_7, CS7)(
                    CHAR_SIZE_8, CS8);
    const map<BaudRate, int>
            SerialPort::simpleSerialBaudRateToPosix =
                    map_list_of(BAUD50, B50) (BAUD75, B75) (BAUD110, B110) (
                            BAUD134, B134) (BAUD150, B150) (BAUD200, B200) (
                            BAUD300, B300) (BAUD600, B600) (BAUD1200, B1200) (
                            BAUD1800, B1800) (BAUD2400, B2400) (BAUD4800, B4800) (
                            BAUD9600, B9600) (BAUD19200, B19200) (BAUD38400, B38400) (
                            BAUD57600, B57600)(BAUD115200, B115200);
    
    SerialPort::SerialPort() :
        fd(CLOSED_FD), maxCharacterDelay(BLOCKING_CHARACTER_DELAY) {
    }
    
    SerialPort::SerialPort(const string &deviceLink, const BaudRate baudRate,
            const CharacterSize characterSize, const FlowControl flowControl,
            const Parity parity, const StopBits stopBits) :
        fd(CLOSED_FD), maxCharacterDelay(BLOCKING_CHARACTER_DELAY) {
        setDeviceLink(deviceLink);
        setBaudRate(baudRate);
        setCharacterSize(characterSize);
        setFlowControl(flowControl);
        setParity(parity);
        setStopBits(stopBits);
    }
    
    SerialPort::~SerialPort() {
        try {
            close();
        } catch (const CloseException &e) {
        }
    }
    
    void SerialPort::open(const string &deviceLink, const BaudRate baudRate,
            const CharacterSize characterSize, const FlowControl flowControl,
            const Parity parity, const StopBits stopBits) throw(CloseException,
            OpenException) {
        if (isOpen()) {
            close();
        }
        setDeviceLink(deviceLink);
        setBaudRate(baudRate);
        setCharacterSize(characterSize);
        setFlowControl(flowControl);
        setParity(parity);
        setStopBits(stopBits);
        open();
    }
    
    void SerialPort::open() throw(OpenException) {
        if (!isOpen()) {
            fd = (::open(deviceLink.c_str(), O_RDWR | O_NOCTTY));
            if(fd < 0) {
                fd = CLOSED_FD;
                throw OpenException();
            }
            applySettings();
        }
    }
    
    void SerialPort::close() throw(CloseException) {
        if(isOpen()) {
            if((::close(fd)) < 0) {
                throw CloseException();
            }
            fd = CLOSED_FD;
        }
    }
    
    void SerialPort::setDeviceLink(const string &deviceLink) {
        this->deviceLink = deviceLink;
    }
    
    void SerialPort::setBaudRate(const BaudRate baudRate) {
        this->baudRate = baudRate;
    }
    
    void SerialPort::setCharacterSize(const CharacterSize characterSize) {
        this->characterSize = characterSize;
    }
    
    void SerialPort::setFlowControl(const FlowControl flowControl) {
        this->flowControl = flowControl;
    }
    
    void SerialPort::setMaxCharacterDelay(const milliseconds &maxCharacterDelay) {
        this->maxCharacterDelay = maxCharacterDelay;
    }
    
    void SerialPort::setParity(const Parity parity) {
        this->parity = parity;
    }
    
    void SerialPort::setStopBits(const StopBits stopBits) {
        this->stopBits = stopBits;
    }
    
    void SerialPort::applySettings() const throw(ControlException, NotOpenException) {
        if(!isOpen()) {
            throw NotOpenException();
        }
        applyGenerals();
        applyBaudRate();
        applyCharacterSize();
        applyFlowControl();
        applyMaxCharacterDelay();
        applyParity();
        applyStopBits();
    }
    
    int SerialPort::write(const string &data) const throw(NotOpenException, WriteException) {
        if(!isOpen()) {
            throw NotOpenException();
        }
        int bytesWritten = (::write(fd, data.c_str(), data.size()));
        if(bytesWritten < 0) {
            throw WriteException();
        }
        return bytesWritten;
    }
    
    void SerialPort::read(string &target, const int bytesToRead) const throw(NotOpenException, ReadException, TimeoutException) {
        if(!isOpen()) {
            throw NotOpenException();
        }
        target = "";
        int bytesLeft = bytesToRead, bytesRead;
        while(bytesLeft > 0) {
            char buffer[bytesLeft];
            bytesRead = (::read(fd, buffer, bytesLeft));
            if(bytesRead == 0) {
                throw TimeoutException();
            } else if(bytesRead < 0) {
                throw ReadException();
            }
            target.append(buffer, bytesRead);
            bytesLeft -= bytesRead;
        }
    }
    
    int SerialPort::readIfAvailable(string &target, const int bytesToRead) const throw(NotOpenException, ReadException) {
        try {
            read(target, bytesToRead);
        } catch(const TimeoutException &ex) {
        }
        return target.size();
    }
    
    const string &SerialPort::getDeviceLink() const {
        return deviceLink;
    }
    
    BaudRate SerialPort::getBaudRate() const {
        return baudRate;
    }
    
    CharacterSize SerialPort::getCharacterSize() const {
        return characterSize;
    }
    
    FlowControl SerialPort::getFlowControl() const {
        return flowControl;
    }
    
    const milliseconds SerialPort::getMaxCharacterDelay() const {
        return maxCharacterDelay;
    }
    
    Parity SerialPort::getParity() const {
        return parity;
    }
    
    StopBits SerialPort::getStopBits() const {
        return stopBits;
    }
    
    bool SerialPort::isOpen() const {
        return fd != CLOSED_FD;
    }
    
    void SerialPort::applyGenerals() const throw(ControlException) {
        struct termios options;
        if(tcgetattr(fd, &options) < 0) {
            throw ControlException();
        }
        options.c_cflag |= (CLOCAL | CREAD);
        options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
        options.c_oflag &= ~ONLCR;
        options.c_iflag &= ~ICRNL;
        if(tcsetattr(fd, TCSAFLUSH, &options) < 0) {
            throw ControlException();
        }
    }
    
    void SerialPort::applyBaudRate() const throw(ControlException) {
        struct termios options;
        if(tcgetattr(fd, &options) < 0) {
            throw ControlException();
        }
        cfsetispeed(&options, simpleSerialBaudRateToPosix.find(baudRate)->second);
        cfsetospeed(&options, simpleSerialBaudRateToPosix.find(baudRate)->second);
        if(tcsetattr(fd, TCSAFLUSH, &options) < 0) {
            throw ControlException();
        }
    }
    
    void SerialPort::applyCharacterSize() const throw(ControlException) {
        struct termios options;
        if(tcgetattr(fd, &options) < 0) {
            throw ControlException();
        }
        options.c_cflag &= ~CSIZE;
        options.c_cflag |= simpleSerialCharacterSizeToPosix.find(characterSize)->second;
        if(tcsetattr(fd, TCSAFLUSH, &options) < 0) {
            throw ControlException();
        }
    }
    
    void SerialPort::applyFlowControl() const throw(ControlException) {
        struct termios options;
        if(tcgetattr(fd, &options) < 0) {
            throw ControlException();
        }
        switch(flowControl) {
            case FLOW_CONTROL_NONE:
            options.c_iflag &= ~(IXON | IXOFF | IXANY);
            options.c_cflag &= ~CRTSCTS;
            break;
            case FLOW_CONTROL_SOFT:
            options.c_iflag |= (IXON | IXOFF | IXANY);
            options.c_cflag &= ~CRTSCTS;
            break;
            case FLOW_CONTROL_HARD:
            options.c_iflag &= ~(IXON | IXOFF | IXANY);
            options.c_cflag |= CRTSCTS;
        }
        if(tcsetattr(fd, TCSAFLUSH, &options) < 0) {
            throw ControlException();
        }
    }
    
    void SerialPort::applyMaxCharacterDelay() const throw(ControlException) {
        struct termios options;
        if(tcgetattr(fd, &options) < 0) {
            throw ControlException();
        }
        options.c_cc[VMIN] = 0;
        options.c_cc[VTIME] = maxCharacterDelay.total_milliseconds() / 100;
        if(tcsetattr(fd, TCSAFLUSH, &options) < 0) {
            throw ControlException();
        }
    }
    
    void SerialPort::applyParity() const throw(ControlException) {
        struct termios options;
        if(tcgetattr(fd, &options) < 0) {
            throw ControlException();
        }
        switch(parity) {
            case NONE: options.c_cflag &= ~PARENB;
            break;
            case EVEN: options.c_cflag |= PARENB;
            options.c_cflag &= ~PARODD;
            break;
            case ODD: options.c_cflag |= PARENB;
            options.c_cflag |= PARODD;
        }
        if(tcsetattr(fd, TCSAFLUSH, &options) < 0) {
            throw ControlException();
        }
    }
    
    void SerialPort::applyStopBits() const throw(ControlException) {
        struct termios options;
        if(tcgetattr(fd, &options) < 0) {
            throw ControlException();
        }
        switch(stopBits) {
            case STOP_BITS_1: options.c_cflag &= ~(CSTOPB);
            break;
            case STOP_BITS_2: options.c_cflag |= CSTOPB;
        }
        if(tcsetattr(fd, TCSAFLUSH, &options) < 0) {
            throw ControlException();
        }
    }
    
    }
    

    Sie läuft sehr viel zuverlässiger als die LibSerial, nichtsdestotrotz taucht dieser Fehler nach längerer Laufzeit auf. In meinem übrigen Code verwende ich allerdings nur Boost shared Pointers, so etwas wie free findet man dort sowieso nicht. Ich könnte mir also nur vorstellen, dass der Fehler etwas mit der nicht Multithread-Fähigkeit einer der Funktionen, die ich hier aufrufe, zu tun hat.

    Weiss jemand, ob das hier der Fall sein könnte? Oder bin ich komplett auf der falschen Fährte? Hat jemand solche double free / corruption Errors schon erlebt?

    Besten Dank für eure Antworten und greeetz
    Kessi


  • Administrator

    Ich glaube kaum, dass es sich jemand antun wird, den Quellcode auf Fehler zu überprüfen, da wir ihn ja nicht einmal ausführen können. Gibt entweder einen deutlich kleineren Beispielquellcode an, wo der selber Fehler immer noch passiert oder ... hmmm, ja, gibt nicht all zu viel oders 🙂

    Das einzige was ich dir sagen kann, ist das was mir die Fehlermeldung sagt. Die Fehlermeldung besagt, dass du einen Speicherbereich zwei mal freigegeben hast oder womöglich über einen Speicherbereich hinausgeschrieben hast, bzw. auf einen ungültigen Bereich geschrieben hast.

    Gerade weil du mehrere Threads hast, solltest du vielleicht mal überprüfen, wann welche Ressourcen freigegeben werden. Wird jede Ressource auch wirklich nur einmal freigegeben und wird sie wirklich erst dann freigegeben, wenn sie nicht mehr benötigt wird?

    Und natürlich auch überall die Speichergrenzen überprüfen und auf wilde Zeiger achten. Also sind alle Zeiger bei der Initialisierung auf NULL gesetzt? Und nachdem sie zerstört wurden auch wieder auf NULL gesetzt? Und wird nirgends über einen Bereich hinausgeschrieben?

    Ich entdecke in deinem Code auf die schnelle auch gar keine Zeiger, wodurch ich vermute, dass der Fehler irgendwo anders passiert und halt, wie es so üblich ist bei korrumpierten Speicher, erst später im Programm auftritt. Ist zum Beispiel das SerialPort Objekt noch gültig, wenn du es verwendest?

    Ich hoffe das wird immerhin etwas weiterhelfen.

    Grüssli



  • valgrind?



  • Dravere schrieb:

    Ich glaube kaum, dass es sich jemand antun wird, den Quellcode auf Fehler zu überprüfen, da wir ihn ja nicht einmal ausführen können.

    Naja, das wollte ich auch gar nicht verlangen 🙂 . Primär ging's mir eigentlich um die POSIX-Funktionen, die ich im Code verwende, ob ich read, write, open etc. eventuell falsch oder unsicher anwende.

    Die restlichen Getter- und Setter-Funktionen sind nicht interessant und müsst ihr mir auch nicht kontrollieren - es ist nur so, dass ich regelmässig angeheissen werde, immer zumindest die ganze Klasse zu posten - unabhängig davon, wo ich den Fehler vermute.

    Was wilde Zeiger angeht, so halte ich mich, wie erwähnt, an das Boost shared_ptr-Pattern. Zeiger werden bei mir nur so erstellt:

    boost::shared_ptr<Type>(new Type())
    

    Ich verwende also gar kein delete und new kommt bei mir nur in shared_ptr-Konstruktoren vor. Deshalb war meine einzige Erklärung bis anhin, dass die paar alten POSIX-Funktionen evtl. noch mit malloc und free arbeiten und z.B. gemeinsame temporäre Buffer bei ihrem Aufruf verwenden, die Multithreading untersagen würden.

    Greeetz



  • wenn du threads und shared_ptr verwendest: lockst du dann auch richtig?



  • Shade Of Mine schrieb:

    wenn du threads und shared_ptr verwendest: lockst du dann auch richtig?

    Nun ja, im Moment ist es so, dass meine Threads nach Schnittstellen aufgeteilt werden. Die Geräte, die über diese Schnittstellen angesprochen werden, bzw. ihre zugehörigen Objekte sind bereits vor der Aufteilung auf die einzelnen Threads erstellt und werden danach nur noch von einem Thread verwendet (da sie ja nur an einer Schnittstelle hängen).

    Es existieren im Programm allerdings dennoch Synchronisationspunkte, z.B. wenn die einzelnen Threads die ausgelesenen Daten der Reihe nach in eine gemeinsame Datei schreiben. Diese Punkte habe ich mit den PnProg-Mitteln, die Boost zur Verfügung stellt (namentlich Mutexe und CyclicBarriers), abgesichert.

    Gibt es denn noch weitere Punkte, die ich bezüglich shared_ptrs beachten müsste?



  • was heißt mehrere thread verwenden, sorry, wollte keine extra thread aufmachen


  • Administrator

    Quellcode schrieb:

    was heißt mehrere thread verwenden, sorry, wollte keine extra thread aufmachen

    Hättest aber besser. Oder noch besser Google benutzt, dann wärst du zum Beispiel hier gelandet:
    http://de.wikipedia.org/wiki/Thread_(Informatik)

    Weiterführende Links kannst du ruhig auch anschauen.

    @KessiMC,
    Tut mir leid, hatte das mit dem shared_ptr falsch verstanden. Dachte, dass du in anderen Projekte den verwendest und nur hier zum ersten Mal nicht. 🙄

    Naja, aber es ist immer noch äusserst schwer dir zu helfen. Probier vielleicht mal ein Log einzuführen und so viele Log-Einträge zu erzeugen wie möglich. Das Programm wird vielleicht extrem lahm, aber plötzlich findet man dann falsche Ausgaben, bzw. welche die nicht vorkommen sollten und trotzdem da sind. Dann kann man anfangen die Sache zurück zu verfolgen.

    Einfache Log-Möglichkeit, zum Beispiel damit:

    class CLog
    {
        // Static functions //
    public:
        static CLog& get_instance() { static CLog Log; return Log; };
    
        // Typedefs //
    private:
        typedef boost::lock_guard<boost::mutex> t_Sync;
    
        // Attributes //
    private:
        std::fstream m_Output;
        boost::mutex m_Mutex;
    
        // Constructor & Destructor //
    protected:
        CLog()
            : m_Output("C:/log.txt") // oder was auch immer
            , m_Mutex()
        { }
    
    public:
        ~CLog() { };
    
        // Methods //
    public:
        template<typename T>
        void write(T const& rValue, bool bNewLine = true) { t_Sync S(m_Mutex); m_Output << rValue << (bNewLine ? std::endl : ""); };
    };
    

    Oder so ähnlich 😉
    Es ist nicht getestet und ich weiss nicht mehr, ob es lock_guard oder locked_guard hiess bei boost.
    Man könnte auch noch die thread-id automatisch mit angeben, Uhrzeit und anderes, vieles möglich. Meistens reichen aber schon nur ein paar strings 😉

    Grüssli


Anmelden zum Antworten