使用无效套接字调用boost :: asio :: write()会使我的Blackberry 10应用程序崩溃

Mos*_*bin 8 c++ sockets boost compiler-optimization blackberry-10

这篇文章涉及最近一个软件项目中遇到的技术问题,并允许读者从问题的来之不易的解决方案中受益.

背景

在我的公司,我是内部库的实现者和维护者,它使用Boost asio("ASynchronous I/O")套接字框架来实现套接字上的跨平台数据传输.一位同事最近遇到了以下问题:如果Wi-Fi路由器在文件传输操作期间被毫不间断地关闭,她的Blackberry 10应用程序会链接并使用我的库,在几秒钟内崩溃.

在库中启用内置跟踪向我们展示了当库调用boost :: asio :: write(boost :: asio :: ip :: tcp :: socket*,boost :: asio :: buffer)时发生崩溃)函数与套接字不是'有效'(即套接字可能无法使用).在write()周围放置一个try/catch(boost :: system :: system_error)块无法捕获任何内容 - 崩溃显然发生在Boost中.

因为崩溃只发生在发布版本中,所以我们无法使用调试器.

技术信息

  • QNX编译器QCC使用GNU 4.6.3
  • 使用的Boost版本是1.48.0

以下是编译器的典型命令行调用:

/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
Run Code Online (Sandbox Code Playgroud)

用于查找问题源的步骤

我们编写了一个轻量级的最小应用程序,尝试用更少的代码重现问题,首先使用原始套接字,然后使用Boost的ASIO.如果崩溃发生,我们可以认为问题不是由我们的专有库引起的.不幸的是,崩溃是不可重复的,导致我们怀疑我们的库有错.

我们编写了一个轻量级跟踪框架,用于Boost的ASIO头文件,用于检测与问题相关的功能.框架在进入和退出这些函数时输出一个字符串,使我们也可以跟踪变量的值.

使用跟踪框架,我们能够证明发生了boost :: throw_exception崩溃()模板函数(非相关#ifdef'd代码中删除).当系统级写入操作在"损坏的管道"上失败时,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));
}
Run Code Online (Sandbox Code Playgroud)

插入的痕迹和分裂"扔"语句为单独的语句向我们证明了坠毁在抛出异常对象的过程中出现.很可能,当抛出异常时,堆栈的展开会出现问题.

解决方案

一旦我们意识到它很可能是编译器错误,而不是应用程序级错误,我们检查了用于构建库的编译器选项.我们排除了内存损坏,因为Boost内部代码可能已经足够强大和强大.一旦我们认为释放模式优化可能是罪魁祸首,距离短的解决方案:将优化级别从下降到-O3 -02.

一旦我们这样做,崩溃就会消失.

我们已经因为修改Blackberry.cmake文件中的QNX工具链使用"-02",而不是原来的"-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")
Run Code Online (Sandbox Code Playgroud)

鉴于此次崩溃,建议谨慎使用"-O3".我们的最小应用程序没有重现问题的原因是因为它是使用优化级别2编译的,而不是3.

我们正在寻找一个SSCCE来提交给QNX和/或GNU团队.

Mos*_*bin 1

完整的解决方案描述可以在问题上方找到。根据戴尔的指示,我在这里提供了解决方案的简要说明。

崩溃是由于使用默认优化级别 3 编译 Boost for Blackberry 造成的。一旦我们将优化级别从 -O3 降至 -O2,崩溃就消失了。

此后,我们修改了 QNX 工具链中的 Blackberry.cmake 文件,以使用“-O2”而不是原始的“-O3”。鉴于这次崩溃,建议谨慎使用“-O3”。