Valgrind在说new的行中看到了删除

Valgrind sees delete in the line that says new

本文关键字:删除 在说 new Valgrind      更新时间:2023-10-16

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

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

作为一个分配和释放内存的地方。此代码是POCO库的一部分,可以在此处找到。下面是valgrind的日志示例,说明提供的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 --vgdb=yes --vgdb-error=0 app

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

我的问题是:

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

@编辑

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

Valgrind消息并不表示您在同一行代码中有一个new和一个delete。

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

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

然后Valgrind试图给你更多关于这段记忆的信息。为此,它向您显示了2个额外的堆栈跟踪:

第二个堆栈跟踪指示在哪里释放了这段内存。第三个堆栈跟踪指示这段内存的分配位置。

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

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

您可能需要查看生成的代码,才能确切地看到为createServiceHandler调用生成了哪些对new/delete的调用。显然,在"新建"answers"删除"堆栈跟踪中有两条不同的指令(0x19D1CD和0x19D19E)。

正如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。这就是为什么应用程序即使在抛出未处理的异常后仍能继续工作的原因。参考代码可以在这里找到:126。