14.3 使用AddressSanifier向CDash报告内存缺陷

NOTE:此示例代码可以在 https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-14/recipe-03 中找到,其中包含一个C++示例和一个Fortran例子。该示例在CMake 3.5版(或更高版本)中是有效的,并且已经在GNU/Linux、macOS和Windows上进行过测试。

AddressSanitizer(ASan)是可用于C++、C和Fortran的内存检测。它可以发现内存缺陷,比如:在空闲后使用、返回后使用、作用域后使用、缓冲区溢出、初始化顺序错误和内存泄漏(请参见 https://github.com/google/sanitizers/wiki/AddressSanitizer )。从3.1版本开始,AddressSanitizer是LLVM的一部分;从4.8版本开始,作为GCC的一部分。在这个示例中,我们将在代码中加入两个bug,正常的测试中可能无法检测到。为了检测这些bug,我们将使用AddressSanitizer工具,并将CTest与动态分析结合起来,从而将缺陷报告给CDash。

准备工作

这个例子中,我们将使用两个源文件和两个测试集:

.
├── CMakeLists.txt
├── CTestConfig.cmake
├── dashboard.cmake
├── src
│    ├── buggy.cpp
│    ├── buggy.hpp
│    └── CMakeLists.txt
└── tests
    ├── CMakeLists.txt
    ├── leaky.cpp
    └── use_after_free.cpp

buggy.cpp包含有两个bug:

#include "buggy.hpp"

#include <iostream>

int function_leaky() {
  double *my_array = new double[1000];
  // do some work ...
  // we forget to deallocate the array
  // delete[] my_array;
  return 0;
}

int function_use_after_free() {
  double *another_array = new double[1000];
  // do some work ...
  // deallocate it, good!
  delete[] another_array;
  // however, we accidentally use the array
  // after it has been deallocated
  std::cout << "not sure what we get: " << another_array[123] << std::endl;
  return 0;
}

这些函数在相应的头文件中声明(buggy.hpp):

测试文件leaky.cpp中将会验证function_leaky的返回值:

相应地,use_after_free.cpp会检查function_use_after_free的返回值:

具体实施

为了使用ASan,我们需要使用特定的标志来编译代码。然后,我们将运行测试并将它们提交到面板。

  1. 生成bug库的工作将在src/CMakeLists.txt中完成:

  2. 在文件src/CMakeLists.txt中,我们将添加一个选项用于使用ASan:

  3. 测试在tests/CMakeLists.txt中定义:

  4. CMakeLists.txt与之前的示例基本相同:

  5. CTestConfig.cmake也没有修改:

  6. 这个示例中,我们使用CTest脚本向CDash提交结果;为此,我们将创建一个文件dashboard.cmake(与主CMakeLists.txt` CTestConfig.cmake位于同一个目录下):

  7. 我们将执行dashboard.cmake脚本。注意,我们使用CTEST_CMAKE_GENERATOR与生成器选项的方式:

  8. 结果将会出现在CDash网站上:

具体实施

这个示例中,成功地向仪表板的动态分析部分报告了内存错误。我们可以通过浏览缺陷详细信息,得到进一步的了解:

通过单击各个链接,可以浏览完整信息的输出。

注意,也可以在本地生成AddressSanitizer报告。这个例子中,我们需要设置ENABLE_ASAN:

运行leaky测试,直接产生以下结果:

相应地,我们可以直接运行use_after_free,得到详细的输出:

如果我们在没有AddressSanitizer的情况下进行测试(默认情况下ENABLE_ASAN是关闭的),就不会报告错误:

实际上,泄漏只会浪费内存,而use_after_free可能会导致未定义行为。调试这些问题的一种方法是使用valgrind (http://valgrind.org )。

与前两个示例相反,我们使用了CTest脚本来配置、构建和测试代码,并将报告提交到面板。要了解此示例的工作原理,请仔细查看dashboard.cmake脚本。首先,我们定义项目名称并设置主机报告和构建名称:

我们的例子中,CTEST_BUILD_NAME的计算结果是Linux-x86_64。不同的操作系统下,可能会观察到不同的结果。

接下来,我们为源和构建目录指定路径:

我们可以将生成器设置为Unix Makefile:

但是,对于更具可移植性的测试脚本,我们更愿意通过命令行提供生成器:

dashboard.cmake中的下一个代码片段,将计算出机器上可用的CPU芯数量,并将测试步骤的并行级设置为可用CPU芯数量,以使总测试时间最小化:

接下来,我们开始测试步骤并配置代码,将ENABLE_ASAN设置为ON:

dashboard.cmake其他命令为映射到构建、测试、内存检查和提交步骤:

更多信息

细心的读者会注意到,在链接目标之前,我们没有在系统上搜索AddressSanitizer。实际中,库查找工作已经提前做完,以避免在链接阶段出现意外。

有关AddressSanitizer文档和示例的更多信息,请参见https://github.com/google/sanitizers/wiki/AddressSanitizer 。AddressSanitizer并不仅限于C和C++。对于Fortran示例,读者可以参考 https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-14/recipe-03/fortran-example

NOTE:可以在https://github.com/arsenm/sanitizers-cmake 上找到CMake程序,用来查找杀毒程序和调整编译器标志

下面的博客文章讨论了如何添加对动态分析工具的支持,对我们很有启发性:https://blog.kitware.com/ctest-cdash-add-support-for-new-dynamic-analysis-tools/

Last updated

Was this helpful?