2017-10-31 4 views
1

Als ich meine Anwendung auf Speicherlecks prüfte, stieß ich auf ein sehr eigenartiges Verhalten von Valgrind. Soweit ich verstehe, sieht er den folgenden CodeValgrind sieht Löschen in der Zeile, die sagt

virtual ServiceHandler* createServiceHandler(StreamSocket& socket) 
    /// Create and initialize a new ServiceHandler instance. 
    /// 
    /// Subclasses can override this method. 
{ 
    return new ServiceHandler(socket, *_pReactor); 
} 

als ein Ort, wo ich sowohl zuweisen und Speicher freigeben. Dieser Code ist Teil der POCO-Bibliothek und kann gefunden werden here. Im Folgenden finden Sie ein Beispiel für das Protokoll von valgrind, das besagt, dass die bereitgestellte return-Anweisung sowohl zu new als auch zu delete führt.

Invalid read of size 8 
==9764== at 0x1C63DC: Poco::Net::Socket::operator!=(Poco::Net::Socket const&) const (Socket.h:340) 
==9764== by 0x1C2A5C: SocketHandler::onReadableNotification(Poco::AutoPtr<Poco::Net::ReadableNotification> const&) (SocketHandler.cpp:67) 
==9764== by 0x1CA0DF: Poco::NObserver<SocketHandler, Poco::Net::ReadableNotification>::notify(Poco::Notification*) const (NObserver.h:88) 
==9764== by 0x5C04077: Poco::NotificationCenter::postNotification(Poco::AutoPtr<Poco::Notification>) (NotificationCenter.cpp:78) 
==9764== by 0x579F422: Poco::Net::SocketNotifier::dispatch(Poco::Net::SocketNotification*) (SocketNotifier.cpp:80) 
==9764== by 0x579C0DC: Poco::Net::SocketReactor::dispatch(Poco::AutoPtr<Poco::Net::SocketNotifier>&, Poco::Net::SocketNotification*) (SocketReactor.cpp:282) 
==9764== by 0x579BEE1: Poco::Net::SocketReactor::dispatch(Poco::Net::Socket const&, Poco::Net::SocketNotification*) (SocketReactor.cpp:258) 
==9764== by 0x579B5F4: Poco::Net::SocketReactor::run() (SocketReactor.cpp:114) 
==9764== by 0x5C50406: Poco::(anonymous namespace)::RunnableHolder::run() (Thread.cpp:57) 
==9764== by 0x5C500E0: Poco::ThreadImpl::runnableEntry(void*) (Thread_POSIX.cpp:349) 
==9764== by 0x6D20493: start_thread (pthread_create.c:333) 
==9764== by 0x78BBAFE: clone (clone.S:97) 
==9764== Address 0x10c8f908 is 40 bytes inside a block of size 352 free'd 
==9764== at 0x4C2D2DB: operator delete(void*) (vg_replace_malloc.c:576) 
==9764== --> by 0x19D1CD: Poco::Net::SocketAcceptor<SocketHandler>::createServiceHandler(Poco::Net::StreamSocket&) (SocketAcceptor.h:166) 
==9764== by 0x19B3E3: Poco::Net::SocketAcceptor<SocketHandler>::onAccept(Poco::Net::ReadableNotification*) (SocketAcceptor.h:157) 
==9764== by 0x1A1385: Poco::Observer<Poco::Net::SocketAcceptor<SocketHandler>, Poco::Net::ReadableNotification>::notify(Poco::Notification*) const (Observer.h:86) 
==9764== by 0x5C04077: Poco::NotificationCenter::postNotification(Poco::AutoPtr<Poco::Notification>) (NotificationCenter.cpp:78) 
==9764== by 0x579F422: Poco::Net::SocketNotifier::dispatch(Poco::Net::SocketNotification*) (SocketNotifier.cpp:80) 
==9764== by 0x579C0DC: Poco::Net::SocketReactor::dispatch(Poco::AutoPtr<Poco::Net::SocketNotifier>&, Poco::Net::SocketNotification*) (SocketReactor.cpp:282) 
==9764== by 0x579BEE1: Poco::Net::SocketReactor::dispatch(Poco::Net::Socket const&, Poco::Net::SocketNotification*) (SocketReactor.cpp:258) 
==9764== by 0x579B5F4: Poco::Net::SocketReactor::run() (SocketReactor.cpp:114) 
==9764== by 0x5C50406: Poco::(anonymous namespace)::RunnableHolder::run() (Thread.cpp:57) 
==9764== by 0x5C500E0: Poco::ThreadImpl::runnableEntry(void*) (Thread_POSIX.cpp:349) 
==9764== by 0x6D20493: start_thread (pthread_create.c:333) 
==9764== Block was alloc'd at 
==9764== at 0x4C2C21F: operator new(unsigned long) (vg_replace_malloc.c:334) 
==9764== --> by 0x19D19E: Poco::Net::SocketAcceptor<SocketHandler>::createServiceHandler(Poco::Net::StreamSocket&) (SocketAcceptor.h:166) 
==9764== by 0x19B3E3: Poco::Net::SocketAcceptor<SocketHandler>::onAccept(Poco::Net::ReadableNotification*) (SocketAcceptor.h:157) 
==9764== by 0x1A1385: Poco::Observer<Poco::Net::SocketAcceptor<SocketHandler>, Poco::Net::ReadableNotification>::notify(Poco::Notification*) const (Observer.h:86) 
==9764== by 0x5C04077: Poco::NotificationCenter::postNotification(Poco::AutoPtr<Poco::Notification>) (NotificationCenter.cpp:78) 
==9764== by 0x579F422: Poco::Net::SocketNotifier::dispatch(Poco::Net::SocketNotification*) (SocketNotifier.cpp:80) 
==9764== by 0x579C0DC: Poco::Net::SocketReactor::dispatch(Poco::AutoPtr<Poco::Net::SocketNotifier>&, Poco::Net::SocketNotification*) (SocketReactor.cpp:282) 
==9764== by 0x579BEE1: Poco::Net::SocketReactor::dispatch(Poco::Net::Socket const&, Poco::Net::SocketNotification*) (SocketReactor.cpp:258) 
==9764== by 0x579B5F4: Poco::Net::SocketReactor::run() (SocketReactor.cpp:114) 
==9764== by 0x5C50406: Poco::(anonymous namespace)::RunnableHolder::run() (Thread.cpp:57) 
==9764== by 0x5C500E0: Poco::ThreadImpl::runnableEntry(void*) (Thread_POSIX.cpp:349) 
==9764== by 0x6D20493: start_thread (pthread_create.c:333) 

Ich habe --> zu dem interessanten Teil hinzugefügt.

Sobald dies passiert, beobachte ich mehr von ähnlichen Protokollen, da das Objekt scheint nicht zugeordnet werden. Die App funktioniert für einige Zeit korrekt, bevor ich diesen Fehler sehe und es ist der erste Fehler. Ich denke, dass dieser Fehler auftreten kann, wenn der Socket nicht nett ist.

I valgrind die App mit dem folgenden Befehl

valgrind --vgdb=yes --vgdb-error=0 app 

und Verwendung gdb weiter den Code zu untersuchen.

Meine Frage lautet:

  • Warum sagt die valgrind mir die bereitgestellten Code neu und löschen ruft?
  • Wird dieses Verhalten lokal von diesem Code verursacht, oder sollte ich das Problem woanders suchen?

@edit

Ctor

SocketHandler::SocketHandler(StreamSocket &socket, SocketReactor &reactor) : 
    _socket(socket) /* copy */, _reactor(reactor) /* reference */ { 

    _socket.setBlocking(false); 

    _reactor.addEventHandler(_socket, NObserver<SocketHandler, ReadableNotification>(*this, 
                       &SocketHandler::onReadableNotification)); 
    _reactor.addEventHandler(_socket, 
         NObserver<SocketHandler, ErrorNotification>(*this, &SocketHandler::onErrorNotification)); 
    _reactor.addEventHandler(_socket, NObserver<SocketHandler, ShutdownNotification>(*this, 
                       &SocketHandler::onShutdownNotification)); 

    _addr = _socket.peerAddress().toString(); 
} 
+0

Die Ctor Nachrichten und löscht dann eine temporäre Objektinstanz? –

+0

Es gibt keine manuellen Zuweisungen in ctor. Ich registriere nur die Rückrufe. Ich werde bald den Code von ctor anhängen. Ich denke auch, dass, wenn so etwas passieren würde, die Stack-Spur länger gewesen wäre – billykun

+0

In Bezug auf die Temp. Instanz, die gelöscht werden könnte, vermute ich '* _pReactor'. Um dies zu beweisen (oder zu widerlegen), wären zwei zusätzliche Dinge notwendig zu wissen: 1. .: Typ von '_pReactor', 2. .: Signatur von' ServiceHandler :: ServiceHandler() '. – Scheff

Antwort

0

Wie von Bo Persson in Kommentaren vorgeschlagen, wurde das Löschen durch eine nicht behandelte Ausnahme im Konstruktor verursacht. Nach entsprechenden Ausnahmen in der Ctor fangen, und Bewegen der Ausdruck, der Ausnahmen vor den Linien, die für die Registrierung der Ereignishandler

SocketHandler::SocketHandler(Poco::Net::StreamSocket &socket, SocketReactor &reactor) : 
     _socket(socket), _reactor(reactor) { 
    try { 
     _socket.setBlocking(false); 
     _addr = _socket.peerAddress().toString(); 

     _reactor.addEventHandler(_socket, NObserver<SocketHandler, ReadableNotification>(*this, 
                         &SocketHandler::onReadableNotification)); 
     _reactor.addEventHandler(_socket, 
           NObserver<SocketHandler, ErrorNotification>(*this, 
                      &SocketHandler::onErrorNotification)); 
     _reactor.addEventHandler(_socket, NObserver<SocketHandler, ShutdownNotification>(*this, 
                         &SocketHandler::onShutdownNotification)); 
    } catch (Exception& e) { 
     // Handle POCO exception 
    } catch (...) { 
     // Handle ellipsis 
    } 
} 

der ungültigen Speicherzugriff ist nicht mehr warf. Die Ausnahme wird von _socket.peerAddress() ausgelöst, wenn versucht wird, eine Adresse eines nicht mehr verbundenen Sockets abzurufen. Zuerst dachte ich, dass jede Ausnahme im ctor die App schnell zum Absturz bringen würde, aber in der Poco::Net::SocketReactor wird jede unbehandelte Ausnahme an eine ErrorHandler übergeben. Aus diesem Grund funktionierte die Anwendung auch nach dem Ablegen nicht behandelter Ausnahmen weiter. Der genannte Code kann here: 126 gefunden werden.

1

Valgrind Nachricht bedeutet nicht, dass Sie mit der gleichen Codezeile ein neues und ein löschen.

Die Valgrind-Fehlermeldung enthält 3 Stack-Traces.

In der ersten Stack-Kurve wird der Fehler erkannt. Dieser Fehler besteht darin, ein Stück Speicher zu lesen, das freigegeben wurde.

Valgrind versucht dann, Ihnen mehr Informationen über dieses Stück Gedächtnis zu geben. Dazu zeigt es Ihnen 2 zusätzliche Stack-Traces:

Der zweite Stack-Trace zeigt an, wo dieses Stück Speicher freigegeben wurde. Der dritte Stack-Trace zeigt an, wo dieser Speicherbereich zugewiesen wurde.

Erklärt mit anderen Worten: Ihr Code hat zuerst ein Stück Speicher zugeordnet (um Stack-Trace Nr. 3). Ihr Code hat dann dieses Speicherstück freigegeben (bei Stack-Trace Nr. 2). Und dann greift Ihr Code falsch auf diesen Speicherbereich zu (bei Stack-Trace Nr. 1).

Also sieht das alles so aus, als ob Sie einen echten Fehler haben: Sie benutzen einen baumelnden Zeiger, der auf freigegebenen Speicher zeigt.

Möglicherweise müssen Sie sich den generierten Code ansehen, um genau zu sehen, welche Aufrufe von new/delete für den Aufruf createServiceHandler generiert werden. Offensichtlich gibt es 2 verschiedene Anweisungen (0x19D1CD und 0x19D19E) in den Stacks 'new' und 'delete'.

+0

außer Trace nr 2 gibt nichts frei - außer es ist ein Nebeneffekt eines malloc, das wäre ungerade – pm100

+1

Laut Valgrind gibt es einen Aufruf zu einem Löschen bei Anweisung 0x19D1CD. Natürlich kann Valgrind einen Bug haben (ich habe noch nie einen solchen Fehler bei falschen Calls gesehen, aber Valgrind Unwind hat manchmal Bugs). Am besten ist es, die Funktion zu zerlegen, die 0x19D19E und 0x19D1CD enthält, und effektiv zu sehen, welche Anrufe da sind. – phd

+0

Ich habe Angst, dass ich nicht weiß genug, um irgendwelche brauchbaren Schlussfolgerungen aus der Demontage in einer angemessenen Zeit zu erhalten. Die richtige Antwort ist wahrscheinlich die, die im Kommentar zu Frage gegeben wird. Auch das Löschen wird durch eine nicht abgefangene Ausnahme verursacht. – billykun

Verwandte Themen