这篇文章讲述了在最近的软件项目中遇到的一个技术问题,并允许读者从辛苦获得的解决方案中获益。
背景
在我的公司里,我是一个内部库的实现者和维护者,它使用Boost asio ("ASynchronous I/O") socket framework实现跨平台的套接字数据传输。最近,我的一位同事遇到了以下问题:她的黑莓10应用程序连接并使用了我的库,如果with路由器在文件传输操作中被随意关闭,它会在几秒钟内崩溃。
在库中启用内置跟踪告诉我们,当库调用boost::asio::write(boost::asio::ip::tcp::socket *,boost::asio::buffer)函数时,会发生崩溃,而套接字不是“有效”的(即,套接字可能不可用)。在write()周围放置一个try/ catch (boost::system::system_error)块没有捕获任何东西--显然,崩溃是在Boost内发生的。
因为崩溃只发生在发布版本中,所以我们无法使用调试器。
技术信息
下面是编译器的典型命令行调用:
/home/foobar/bbndk/host_10_1_0_238/linux/x86/usr/bin/QCC
-Vgcc_ntoarmv7le
-lang-c++
-x c++
-DLINUX -DQNX -DSUPPORT_LAN -DUSE_SQLITE_FOR_DATABASE
-Wno-psabi -Wno-write-strings
-O3
-DNDEBUG
-fno-strict-aliasing
-fPIC
-I/home/foobar/Libraries/BlackBerry_10/boost_1.48/include
...
-I/home/foobar/Libraries/BlackBerry_10/utfcpp_1.0/include
-o CMakeFiles/Internals.dir/ConfigFileSingleton.cpp.o
-c /home/foobar/myproject_dev/myproject/SDK/Internals/ConfigFileSingleton.cpp用于定位问题源的步骤
我们编写了一个轻量级的最小应用程序,尝试用更少的代码再现问题,首先使用原始套接字,然后使用Boost的ASIO。如果崩溃发生,我们可以假设问题不是由我们的专有库引起的。不幸的是,这次坠机是不可复制的,导致我们怀疑我们的图书馆是错误的。
我们编写了一个轻量级跟踪框架,以便在Boost的ASIO头文件中使用,检测与问题相关的函数。该框架在输入和退出这些函数时输出一个字符串,使我们也能够跟踪变量的值。
使用跟踪框架,我们能够证明崩溃发生在boost::throw_exception()模板函数中(与此无关的#ifdef‘s代码删除)。当系统级写操作在“断管”上失败时,Boost调用此函数:
template<class E> BOOST_ATTRIBUTE_NORETURN inline void throw_exception( E const & e )
{
//All boost exceptions are required to derive from std::exception,
//to ensure compatibility with BOOST_NO_EXCEPTIONS.
throw_exception_assert_compatibility(e);
throw enable_current_exception(enable_error_info(e));
}插入跟踪并将“抛出”语句拆分为单独的语句向我们证明,崩溃发生在抛出异常对象的过程中。很可能,当异常被抛出时,堆栈的展开发生了很大的问题。
解决方案
一旦我们意识到它很可能是编译器错误,而不是应用程序级错误,我们就检查了用于构建库的编译器选项。我们排除了内存损坏的可能性,因为Boost内部代码可能已经增强并且足够健壮。一旦我们认为发布模式优化可能是罪魁祸首,那么与解决方案的距离就很短了:将优化级别从-O3降到-O2。
一旦我们这么做了,坠机就消失了。
此后,我们修改了QNX工具链中的Blackberry.cmake文件,使其使用"-O2“,而不是原始的"-O3":
SET(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG")
#SET(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG")
. . .
SET(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG -fno-strict-aliasing -fPIC")
#SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG -fno-strict-aliasing -fPIC")考虑到这种崩溃,谨慎使用"-O3“可能是明智的。我们最小的应用程序没有重现问题的原因是因为它是用优化级别2而不是3编译的。
我们正在寻找一个提交给QNX和/或GNU团队的SSCCE。
发布于 2014-04-19 17:49:27
以上问题中可以找到完整的解决方案描述。根据戴尔的指针,我在这里提供了一个解决方案的简要说明。
这次崩溃是由于使用默认的优化级别3为黑莓编译Boost。一旦我们将优化级别从-O3降到-O2,崩溃就消失了。
此后,我们修改了QNX工具链中的Blackberry.cmake文件,使其使用"-O2“而不是原始的"-O3”。考虑到这种崩溃,谨慎使用"-O3“可能是明智的。
https://stackoverflow.com/questions/21886085
复制相似问题