避免在 Boost Log 琐碎的使用中泄漏
Avoid leak in Boost Log trivial usage
我从使用随 boost 1.56 分发的 boostlog 的服务器端应用程序获取 valgrind 泄漏报告。 valgrind 报告是:
==8021== 1,159个块中的37,088个字节肯定丢失在丢失记录1,613 of 1,642中
==8021== 位于 0x4A05588:memalign (vg_replace_malloc.c:727)
==8021== 通过 0x3FDA61118F:tls_get_addr_tail(在 /lib64/ld-2.12.so)
==8021== 通过 0x3FDA61165F:__tls_get_addr(在 /lib64/ld-2.12.so)
==8021== 通过 0x3FE6ABBDCB:__cxa_get_globals(在 /usr/lib64/libstdc++.so.6.0.13 中)
==8021== 通过 0x730C528:boost::log::v2_mt_posix::aux::unhandled_exception_count()(在 /opt/sesteksdk/lib/libboost_log.so.1.56.0 中)
==8021== by 0x5D54D1F: sestek::mrcp::audio::recognition::AsynchronousRecognizer::Notify(sestek::voice::recognition::IRecognizerNotification const*) (record_ostream.hpp:259)
此泄漏来自简单的一行:
LOGGER(debug)<< _chanProp->GetId() << " got recognition ended notification from recognizer";
我们仅通过一次短暂的测试就得到了 5 个这样的泄漏 运行。
我们使用文本文件后端,同步接收器,自动刷新开启。基本上:
void InitializeFileLog(const std::string & logDir)
{
boost::shared_ptr< logging::core > loggingCore = logging::core::get();
loggingCore->add_global_attribute("TimeStamp", attrs::local_clock());
string logPath = logDir + "/gvzmrcpsr_%N.txt";
boost::shared_ptr< sinks::text_file_backend > backend =
boost::make_shared< sinks::text_file_backend >(
// file name pattern
keywords::file_name = logPath,
// rotate the file upon reaching 5 MiB size...
keywords::rotation_size = 5 * 1024 * 1024,
// ...or at midnight, whichever comes first
keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0)
);
backend->auto_flush(true);
// Wrap it into the frontend and register in the core.
// The backend requires synchronization in the frontend.
typedef sinks::synchronous_sink< sinks::text_file_backend > sink_t;
boost::shared_ptr< sink_t > sink = boost::make_shared< sink_t>(backend);
loggingCore->add_sink(sink);
sink->flush();
sink->set_formatter
(
expr::stream
<< expr::attr< boost::posix_time::ptime >("TimeStamp")
<< " : [" << expr::attr< sestek::log::LogLevel >("Severity")
<< "] " << expr::smessage
);
backend->set_file_collector(sinks::file::make_collector(
// rotated logs will be moved here
keywords::target = logDir + "/old_mrcpsr_plugin_logs",
// oldest log files will be removed if the total size reaches 100 MiB...
keywords::max_size = 100 * 1024 * 1024,
// ...or the free space in the target directory comes down to 50 MiB
keywords::min_free_space = 50 * 1024 * 1024
));
try
{
backend->scan_for_files(sinks::file::scan_all);
}
catch(std::exception & )
{
//LOGGER(sestek::log::fatal) << "exception during scanning : " << e.what();
}
}
系统编译运行在centos 6.6上使用devtoolkit2.0。 gcc 版本是 4.8.2。
那么是不是我们使用boost log有问题?或者boost log真的有这样的问题。我认为我们的用法可以认为是微不足道的,我们只是在启动时 运行 上面的配置代码。
注意:尽管单次泄漏可能足够小,但我们的软件 运行 作为服务器上的一项服务,因此这种重复泄漏对我们来说是有问题的。
我真的不明白这个问题。您出示泄漏的证据,并询问 "does it leak"。嗯,是。这不足为奇。记录器使用本地线程 "singletons"。根据您组织线程的方式,possible/nigh 不可能正确地拆除它们。
是时候制作 SSCCE 并查看有关正确关机顺序的文档了。
备注
众所周知,关闭记录器非常困难。您需要处理关闭期间需要记录某些内容的机会(设计气味);更糟糕的是,不同的接收器可能相互依赖并阻止以任何特定顺序关闭)。
相当多的框架只是将其留给 OS 进行清理。
PS 没有任何迹象表明重复泄漏,因为它看起来像是每个线程的泄漏。
Boost Log - 与许多其他日志记录库一样 - 在内部使用 tls。当线程终止时,日志系统很难(有时似乎不可能)清理 tls 变量。 Boost面临同样的困难。
对于包含日志记录代码的长 运行 应用程序,分离许多线程并在任务完成后终止它们不是一个好的用途。在繁重的多任务系统中,更好的方法是使用线程池,而不是每次都启动新线程。
我将应用程序转换为使用线程池,问题中的漏洞已经消失。 Tls 变量仍然存在,但由于现在线程被重用,tls 变量也被其对应的线程重用。
泄漏的对象是 C++ 运行时的内部部分,它不是由 Boost.Log 显式创建的。据我所见,该对象是按线程创建的,因此应在线程终止时销毁。你对 Boost.Log 的使用对我来说似乎没问题。
我从使用随 boost 1.56 分发的 boostlog 的服务器端应用程序获取 valgrind 泄漏报告。 valgrind 报告是:
==8021== 1,159个块中的37,088个字节肯定丢失在丢失记录1,613 of 1,642中
==8021== 位于 0x4A05588:memalign (vg_replace_malloc.c:727)
==8021== 通过 0x3FDA61118F:tls_get_addr_tail(在 /lib64/ld-2.12.so)
==8021== 通过 0x3FDA61165F:__tls_get_addr(在 /lib64/ld-2.12.so)
==8021== 通过 0x3FE6ABBDCB:__cxa_get_globals(在 /usr/lib64/libstdc++.so.6.0.13 中)
==8021== 通过 0x730C528:boost::log::v2_mt_posix::aux::unhandled_exception_count()(在 /opt/sesteksdk/lib/libboost_log.so.1.56.0 中)
==8021== by 0x5D54D1F: sestek::mrcp::audio::recognition::AsynchronousRecognizer::Notify(sestek::voice::recognition::IRecognizerNotification const*) (record_ostream.hpp:259)
此泄漏来自简单的一行:
LOGGER(debug)<< _chanProp->GetId() << " got recognition ended notification from recognizer";
我们仅通过一次短暂的测试就得到了 5 个这样的泄漏 运行。
我们使用文本文件后端,同步接收器,自动刷新开启。基本上:
void InitializeFileLog(const std::string & logDir)
{
boost::shared_ptr< logging::core > loggingCore = logging::core::get();
loggingCore->add_global_attribute("TimeStamp", attrs::local_clock());
string logPath = logDir + "/gvzmrcpsr_%N.txt";
boost::shared_ptr< sinks::text_file_backend > backend =
boost::make_shared< sinks::text_file_backend >(
// file name pattern
keywords::file_name = logPath,
// rotate the file upon reaching 5 MiB size...
keywords::rotation_size = 5 * 1024 * 1024,
// ...or at midnight, whichever comes first
keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0)
);
backend->auto_flush(true);
// Wrap it into the frontend and register in the core.
// The backend requires synchronization in the frontend.
typedef sinks::synchronous_sink< sinks::text_file_backend > sink_t;
boost::shared_ptr< sink_t > sink = boost::make_shared< sink_t>(backend);
loggingCore->add_sink(sink);
sink->flush();
sink->set_formatter
(
expr::stream
<< expr::attr< boost::posix_time::ptime >("TimeStamp")
<< " : [" << expr::attr< sestek::log::LogLevel >("Severity")
<< "] " << expr::smessage
);
backend->set_file_collector(sinks::file::make_collector(
// rotated logs will be moved here
keywords::target = logDir + "/old_mrcpsr_plugin_logs",
// oldest log files will be removed if the total size reaches 100 MiB...
keywords::max_size = 100 * 1024 * 1024,
// ...or the free space in the target directory comes down to 50 MiB
keywords::min_free_space = 50 * 1024 * 1024
));
try
{
backend->scan_for_files(sinks::file::scan_all);
}
catch(std::exception & )
{
//LOGGER(sestek::log::fatal) << "exception during scanning : " << e.what();
}
}
系统编译运行在centos 6.6上使用devtoolkit2.0。 gcc 版本是 4.8.2。
那么是不是我们使用boost log有问题?或者boost log真的有这样的问题。我认为我们的用法可以认为是微不足道的,我们只是在启动时 运行 上面的配置代码。
注意:尽管单次泄漏可能足够小,但我们的软件 运行 作为服务器上的一项服务,因此这种重复泄漏对我们来说是有问题的。
我真的不明白这个问题。您出示泄漏的证据,并询问 "does it leak"。嗯,是。这不足为奇。记录器使用本地线程 "singletons"。根据您组织线程的方式,possible/nigh 不可能正确地拆除它们。
是时候制作 SSCCE 并查看有关正确关机顺序的文档了。
备注
众所周知,关闭记录器非常困难。您需要处理关闭期间需要记录某些内容的机会(设计气味);更糟糕的是,不同的接收器可能相互依赖并阻止以任何特定顺序关闭)。
相当多的框架只是将其留给 OS 进行清理。
PS 没有任何迹象表明重复泄漏,因为它看起来像是每个线程的泄漏。
Boost Log - 与许多其他日志记录库一样 - 在内部使用 tls。当线程终止时,日志系统很难(有时似乎不可能)清理 tls 变量。 Boost面临同样的困难。
对于包含日志记录代码的长 运行 应用程序,分离许多线程并在任务完成后终止它们不是一个好的用途。在繁重的多任务系统中,更好的方法是使用线程池,而不是每次都启动新线程。
我将应用程序转换为使用线程池,问题中的漏洞已经消失。 Tls 变量仍然存在,但由于现在线程被重用,tls 变量也被其对应的线程重用。
泄漏的对象是 C++ 运行时的内部部分,它不是由 Boost.Log 显式创建的。据我所见,该对象是按线程创建的,因此应在线程终止时销毁。你对 Boost.Log 的使用对我来说似乎没问题。