Valgrind看到删除行中的新内容

时间:2017-10-31 12:01:47

标签: c++ valgrind poco-libraries

当我检查我的应用程序是否有内存泄漏时,我遇到了valgrind非常特殊的行为。据我所知,他看到以下代码

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

作为我分配和释放记忆的地方。此代码是POCO库的一部分,可以找到它here。下面是来自valgrind的log的一个例子,说明提供的return语句同时产生new和delete。

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)

我将-->添加到有趣的部分。

一旦发生这种情况,我会观察到更多类似的日志,因为对象似乎没有被分配。在看到此错误之前,应用程序正常工作了一段时间,这是第一个发生的错误。我认为当套接字行为不好时可能会发生此错误。

我使用以下命令对应用程序进行valgrind

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

并使用gdb进一步检查代码。

我的问题是:

  • 为什么valgrind告诉我提供的代码调用new和delete?
  • 此行为是由本地代码引起的,还是应该在其他地方查找问题?

@edit

构造函数

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();
}

2 个答案:

答案 0 :(得分:1)

Valgrind消息并不表示您在同一行代码中有新消息和删除消息。

valgrind错误消息包含3个堆栈跟踪。

第一个堆栈跟踪是检测到错误的位置。此错误包括读取已释放的内存。

然后,Valgrind试图为您提供有关此内存的更多信息。 为此,它会显示2个额外的堆栈跟踪:

第二个堆栈跟踪指示释放此内存的位置。 第三个堆栈跟踪指示这块内存的分配位置。

换句话说:你的代码首先分配了一块内存(at 堆栈跟踪nr 3)。然后你的代码释放了这段内存(在堆栈跟踪nr 2)。 然后你的代码错误地访问了这块内存(在堆栈跟踪nr 1处)。

所以,这看起来就像你有一个真正的错误:你正在使用一个悬空指针,指向已释放的内存。

您可能必须查看生成的代码,以确切了解为createServiceHandler调用生成了对new / delete的调用。 显然,'new'和'delete'堆栈跟踪中有2条不同的指令(0x19D1CD和0x19D19E)。

答案 1 :(得分:0)

正如Bo Persson在评论中所建议的,删除是由构造函数中的未处理异常引起的。在ctor中捕获相关异常之后,在负责注册事件处理程序的行之前移动抛出异常的表达式

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
    }
}

无效的内存访问不再存在。尝试获取不再连接的套接字地址时,_socket.peerAddress()抛出异常。起初我认为ctor中的任何异常都会导致应用程序迅速崩溃,但在Poco::Net::SocketReactor中,每个未处理的异常都会传递给ErrorHandler。这就是为什么应用程序在抛出未处理的异常后仍然继续工作的原因。可以找到引用的代码here:126。