Поиск утечки с помощью vld

The_REAL
Дата: 18.08.2014 22:03:29
Программа-сервер, многопоточная, не могу понять - где\в чем утечка:
включаю в код
#include "vld.h"
и получаю в консоли сотни "утечек" вида:
---------- Block 6127 at 0x00CEDF30: 76 bytes ----------
Call Stack:
c:\program files\boost\boost_1_51\boost\asio\detail\impl\strand_service.ipp (84): SkyRiverServerMT.exe!boost::asio::detail::strand_service::construct + 0x7 bytes
c:\program files\boost\boost_1_51\boost\asio\strand.hpp (97): SkyRiverServerMT.exe!boost::asio::io_service::strand::strand
d:\users\android\skyriverserver\skyriverservermt\logisticserver.cpp (140): SkyRiverServerMT.exe!connection::connection + 0x6F bytes
d:\users\android\skyriverserver\skyriverservermt\logisticserver.cpp (306): SkyRiverServerMT.exe!server3::start_accept + 0x35 bytes
d:\users\android\skyriverserver\skyriverservermt\logisticserver.cpp (321): SkyRiverServerMT.exe!server3::handle_accept
c:\program files\boost\boost_1_51\boost\bind\mem_fn_template.hpp (165): SkyRiverServerMT.exe!boost::_mfi::mf1<void,server3,boost::system::error_code const &>::operator() + 0x10 bytes
c:\program files\boost\boost_1_51\boost\bind\bind.hpp (314): SkyRiverServerMT.exe!boost::_bi::list2<boost::_bi::value<server3 *>,boost::arg<1> >::operator()<boost::_mfi::mf1<void,server3,boost::system::error_code const &>,boost::_bi::list1<boost::system::error_code const &> >
c:\program files\boost\boost_1_51\boost\bind\bind_template.hpp (48): SkyRiverServerMT.exe!boost::_bi::bind_t<void,boost::_mfi::mf1<void,server3,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<server3 *>,boost::arg<1> > >::operator()<boost::system::error_code>
c:\program files\boost\boost_1_51\boost\asio\detail\bind_handler.hpp (47): SkyRiverServerMT.exe!boost::asio::detail::binder1<boost::_bi::bind_t<void,boost::_mfi::mf1<void,server3,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<server3 *>,boost::arg<1> > >,boost::system::error_code>::operator()
c:\program files\boost\boost_1_51\boost\asio\handler_invoke_hook.hpp (65): SkyRiverServerMT.exe!boost::asio::asio_handler_invoke<boost::asio::detail::binder1<boost::_bi::bind_t<void,boost::_mfi::mf1<void,server3,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<server3 *>,boost::arg<1> > >,boost::system::error_code> >
c:\program files\boost\boost_1_51\boost\asio\detail\handler_invoke_helpers.hpp (39): SkyRiverServerMT.exe!boost_asio_handler_invoke_helpers::invoke<boost::asio::detail::binder1<boost::_bi::bind_t<void,boost::_mfi::mf1<void,server3,boost::system::error_code const &>,boost::_bi::list2<boost::_bi::value<server3 *>,boost::arg<1> > >,boost::system::error_code>,boo + 0x30 bytes
c:\program files\boost\boost_1_51\boost\asio\detail\win_iocp_socket_accept_op.hpp (142): SkyRiverServerMT.exe!boost::asio::detail::win_iocp_socket_accept_op<boost::asio::basic_socket<boost::asio::ip::tcp,boost::asio::stream_socket_service<boost::asio::ip::tcp> >,boost::asio::ip::tcp,boost::_bi::bind_t<void,boost::_mfi::mf1<void,server3,boost::system::error_code c + 0x13 bytes
c:\program files\boost\boost_1_51\boost\asio\detail\win_iocp_operation.hpp (45): SkyRiverServerMT.exe!boost::asio::detail::win_iocp_operation::complete + 0x1A bytes
c:\program files\boost\boost_1_51\boost\asio\detail\impl\win_iocp_io_service.ipp (397): SkyRiverServerMT.exe!boost::asio::detail::win_iocp_io_service::do_one
c:\program files\boost\boost_1_51\boost\asio\detail\impl\win_iocp_io_service.ipp (159): SkyRiverServerMT.exe!boost::asio::detail::win_iocp_io_service::run + 0xE bytes
c:\program files\boost\boost_1_51\boost\asio\impl\io_service.ipp (59): SkyRiverServerMT.exe!boost::asio::io_service::run + 0xF bytes
c:\program files\boost\boost_1_51\boost\bind\mem_fn_template.hpp (49): SkyRiverServerMT.exe!boost::_mfi::mf0<unsigned int,boost::asio::io_service>::operator() + 0xC bytes
c:\program files\boost\boost_1_51\boost\bind\bind.hpp (244): SkyRiverServerMT.exe!boost::_bi::list1<boost::_bi::value<boost::asio::io_service *> >::operator()<unsigned int,boost::_mfi::mf0<unsigned int,boost::asio::io_service>,boost::_bi::list0>
c:\program files\boost\boost_1_51\boost\bind\bind_template.hpp (21): SkyRiverServerMT.exe!boost::_bi::bind_t<unsigned int,boost::_mfi::mf0<unsigned int,boost::asio::io_service>,boost::_bi::list1<boost::_bi::value<boost::asio::io_service *> > >::operator()
c:\program files\boost\boost_1_51\boost\thread\detail\thread.hpp (75): SkyRiverServerMT.exe!boost::detail::thread_data<boost::_bi::bind_t<unsigned int,boost::_mfi::mf0<unsigned int,boost::asio::io_service>,boost::_bi::list1<boost::_bi::value<boost::asio::io_service *> > > >::run
e:\installer\boost\libs\thread\src\win32\thread.cpp (191): SkyRiverServerMT.exe!boost::`anonymous namespace'::thread_start_function
f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c (348): SkyRiverServerMT.exe!_callthreadstartex + 0xF bytes
f:\dd\vctools\crt_bld\self_x86\crt\src\threadex.c (331): SkyRiverServerMT.exe!_threadstartex
0x7C80B729 (File and line number not available): kernel32.dll!GetModuleFileNameA + 0x1BA bytes


Единственное что "моё" это строки
d:\users\android\skyriverserver\skyriverservermt\logisticserver.cpp (140): SkyRiverServerMT.exe!connection::connection + 0x6F bytes
d:\users\android\skyriverserver\skyriverservermt\logisticserver.cpp (306): SkyRiverServerMT.exe!server3::start_accept + 0x35 bytes
d:\users\android\skyriverserver\skyriverservermt\logisticserver.cpp (321): SkyRiverServerMT.exe!server3::handle_accept

где
140 это конструктор connection
306 это строка new_connection_.reset(new connection(io_service_));
321 это кавычка, закрывающая handle_accept...

которые вообщем то взяты из примера библиотеки буст... весь текст:
class connection : public boost::enable_shared_from_this<connection>, private boost::noncopyable
{
public:
	connection(boost::asio::io_service& io_service)
	  : strand_(io_service),
		socket_(io_service),
		pTerminalHandler_(NULL),
		bCloseConnect_(false)
	{
	}

	void self_delete()
	{
		boost::system::error_code ignored_ec;
		socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
		socket_.close();

		if(pTerminalHandler_)
		{
			delete pTerminalHandler_;
		}	
	}
	/// Get the socket associated with the connection.
	boost::asio::ip::tcp::socket& socket()
	{
		return socket_;
	}
	void start()
	{
		socket_.async_read_some(boost::asio::buffer(buffer_, 1024),
		  strand_.wrap(
			boost::bind(&connection::handle_read, shared_from_this(),
			  boost::asio::placeholders::error,
			  boost::asio::placeholders::bytes_transferred)));
	}
	void connection::handle_read(const boost::system::error_code& e, std::size_t bytes_transferred)
	{
		if (!e)
		{
 			string strData(buffer_.data(), bytes_transferred);

			if(!pTerminalHandler_)
			{
				pTerminalHandler_ = CTerminalHandlerSelector::SelectTerminal(strData);

				if(!pTerminalHandler_)
				{
					self_delete();
					return;
				}
			}

			bCloseConnect_ = pTerminalHandler_->ReadData(strData);

			if(bCloseConnect_)
			{
				self_delete();
				return;
			}

			bCloseConnect_ = pTerminalHandler_->WriteData(strData);

			if(strData.length())
			{
				 boost::asio::async_write(socket_, boost::asio::buffer(strData.c_str(), strData.length()),
				  strand_.wrap(boost::bind(&connection::handle_write, shared_from_this(),
					  boost::asio::placeholders::error)));
			}
			else if(!bCloseConnect_)
			{
				start();			
			}
			else
			{
				self_delete();
			}
		}
	}
	void connection::handle_write(const boost::system::error_code& e)
	{
		if (!e)
		{
			if(bCloseConnect_)
			{
				self_delete();
				return;
			}
			start();
		}
	}
private:
	boost::asio::io_service::strand strand_;
	boost::asio::ip::tcp::socket socket_;
	boost::array<char, 1024> buffer_;
	
	//sean
	bool bCloseConnect_;
	CTerminalHandler* pTerminalHandler_;
};

typedef boost::shared_ptr<connection> connection_ptr;

class server3 : private boost::noncopyable
{
public:
	server3(const std::string& address, const std::string& port, std::size_t thread_pool_size)
		: thread_pool_size_(thread_pool_size),
		signals_(io_service_),
		acceptor_(io_service_),
		new_connection_()
	{
		signals_.add(SIGINT);
		signals_.add(SIGTERM);
		#if defined(SIGQUIT)
		  signals_.add(SIGQUIT);
		#endif // defined(SIGQUIT)
		signals_.async_wait(boost::bind(&server3::handle_stop, this));

		boost::asio::ip::tcp::resolver resolver(io_service_);
		boost::asio::ip::tcp::resolver::query query(address, port);
		boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
		acceptor_.open(endpoint.protocol());
		acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
		acceptor_.bind(endpoint);
		acceptor_.listen();

		start_accept();
	}

	void run()
	{
		std::vector<boost::shared_ptr<boost::thread> > threads;
		for (std::size_t i = 0; i < thread_pool_size_; ++i)
		{
			boost::shared_ptr<boost::thread> thread(new boost::thread(boost::bind(&boost::asio::io_service::run, &io_service_)));
			threads.push_back(thread);
		}

		// Wait for all threads in the pool to exit.
		for (std::size_t i = 0; i < threads.size(); ++i)
			threads[i]->join();
	}

	void start_accept()
	{
		new_connection_.reset(new connection(io_service_));
		acceptor_.async_accept(new_connection_->socket(), boost::bind(&server3::handle_accept, this, boost::asio::placeholders::error));
	}

	void handle_accept(const boost::system::error_code& e)
	{
		if (!e)
		{
			new_connection_->start();
		}

		start_accept();
	}

	void handle_stop()
	{
		io_service_.stop();
	}
private:
	std::size_t thread_pool_size_;
	boost::asio::io_service io_service_;
	boost::asio::signal_set signals_;
	boost::asio::ip::tcp::acceptor acceptor_;
	connection_ptr new_connection_;
};
Anatoly Moskovsky
Дата: 18.08.2014 23:54:55
The_REAL
весь текст:

Какой же это "весь текст", когда тут как минимум нет текста main() и класса CTerminalHandlerSelector?

Из того что приведено, видно две потенциальные проблемы.
1) delete pTerminalHandler_ не всегда вызывается. Например при ошибке в connection::handle_read
Используйте деструктор для гарантированного удаления ресурсов класса.
2) Судя по названию CTerminalHandlerSelector::SelectTerminal объект pTerminalHandler_ не создается а выбирается из списка готовых. Если это так то удалять его вообще не нужно - будет обращение к удаленному объекту, что тоже в принципе утечка памяти.
The_REAL
Дата: 19.08.2014 11:08:14
Меня больше интересует, почему vld ругается на утечку именно в этом месте?

Anatoly Moskovsky, спасибо что глянули код - дело в том, да там есть ньюансы, но...
1) да, вписывал уничтожение в деструктор connection - проблему не решило
2) ну, там создается объект, просто класса-наследника, одного из нескольких.

дело в том, что остальной код - если заменить выделенный фрагмент на однопоточную реализацию без пула (так же есть в примерах boost asio) работает без утечек (ну может очень редких, при ошибке в connection::handle_read как вы сказали), в отличии от этого - тут память "уходит" мегабайтами в час :(