CMake 完整使用教程 之十五 测试面板
本文于1450天之前发表,文中内容可能已经过时。
本章的主要内容有:
- 将测试部署到CDash面板
- CDash面板显示测试覆盖率
- 使用AddressSanifier向CDash报告内存缺陷
- 使用ThreadSaniiser向CDash报告数据争用
CDash是一个web服务,用于汇集CTest在测试运行期间、夜间测试期间或在持续集成中的测试结果。
本章中,我们将向CDash报告测试结果。将讨论报告测试覆盖率的策略,以及分别使用AddressSanifier和ThreadSanifier等工具,收集的内存缺陷和数据争用问题。
有两种方法向CDash报告结果:
- 通过构建的测试目标
- 使用CTest脚本
在前两个示例中使用建立测试目标的方式,在后两个示例中使用CTest脚本。
CDash环境
CDash的安装需要使用PHP和SSL的web服务器(Apache、NGINX或IIS),并访问MySQL或PostgreSQL数据库服务器。详细讨论CDash web服务的设置超出了本书的范围,读者们可以参考官方文档:https://public.kitware.com/Wiki/CDash:Installation
Kitware提供了两个面板(https://my.cdash.org 和 https://open.cdash.org ),因此本章中的示例并不需要安装CDash。我们将在示例中参考已经提供的面板。
对于想要自己安装CDash的读者,我们建议使用MySQL作为后端,因为这是 https://my.cdash.org 和 https://open.cdash.org 的配置方式,而且社区也对这种搭配方式进行了测试。
NOTE:也可以使用Docker来安装CDash。官方镜像的请求在CDash的跟踪器上处于打开状态,网址是https://github.com/Kitware/CDash/issues/562
14.1 将测试部署到CDash
NOTE:此示例代码可以在 https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-14/recipe-01 中找到,其中包含一个C++示例。该示例在CMake 3.5版(或更高版本)中是有效的,并且已经在GNU/Linux、macOS和Windows上进行过测试。
本示例中,我们将扩展第4章第1节的测试示例,并将测试结果部署到https://my.cdash.org/index.php?project=cmake-cookbook ,这是在Kitware为社区提供的公共面板( https://my.cdash.org )的基础上,为本书创建的专属面板。
准备工作
我们将从重用第1节中的示例源代码,该测试将整数作为命令行参数进行求和。该示例由三个源文件组成:main.cpp
、sum_integer.cpp
和sum_integers.hpp
。我们还将重用第4章(创建和运行测试)中的test.cpp
文件,但这里将它重命名为test_short.cpp
。我们将使用test_long.cpp
扩展这个例子:
1 |
|
然后,将这些文件组织成以下文件树:
1 | . |
具体实施
现在,我们将演示如何配置、构建、测试。最后,将示例项目的测试结果提交到面板的过程:
源目标在
src/CMakeLists.txt
中定义,如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19# example library
add_library(sum_integers "")
target_sources(sum_integers
PRIVATE
sum_integers.cpp
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/sum_integers.hpp
)
target_include_directories(sum_integers
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
# main code
add_executable(sum_up main.cpp)
target_link_libraries(sum_up sum_integers)tests/CMakeLists.txt
中定义了测试:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19add_executable(test_short test_short.cpp)
target_link_libraries(test_short sum_integers)
add_executable(test_long test_long.cpp)
target_link_libraries(test_long sum_integers)
add_test(
NAME
test_short
COMMAND
$<TARGET_FILE:test_short>
)
add_test(
NAME
test_long
COMMAND
$<TARGET_FILE:test_long>
)主
CMakeLists.txt
文件引用前面的两个文件,这个配置中的新元素是include(CTest)
,这样就可以向CDash仪表板报告结果:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20# set minimum cmake version
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
# project name and language
project(recipe-01 LANGUAGES CXX)
# require C++11
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# process src/CMakeLists.txt
add_subdirectory(src)
enable_testing()
# allow to report to a cdash dashboard
include(CTest)
# process tests/CMakeLists.txt
add_subdirectory(tests)另外,我们创建文件
CTestConfig.cmake
与主CMakeLists.txt
文件位于同一目录中。这个新文件包含以下几行:1
2
3
4set(CTEST_DROP_METHOD "http")
set(CTEST_DROP_SITE "my.cdash.org")
set(CTEST_DROP_LOCATION "/submit.php?project=cmake-cookbook")
set(CTEST_DROP_SITE_CDASH TRUE)我们现在已经准备好配置和构建项目:
1
2
3
4$ mkdir -p build
$ cd build
$ cmake ..
$ cmake --build .构建后,运行测试集,并向面板报告测试结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30$ ctest --dashboard Experimental
Site: larry
Build name: Linux-c++
Create new tag: 20180408-1449 - Experimental
Configure project
Each . represents 1024 bytes of output
. Size of output: 0K
Build project
Each symbol represents 1024 bytes of output.
'!' represents an error and '*' a warning.
. Size of output: 0K
0 Compiler errors
0 Compiler warnings
Test project /home/user/cmake-recipes/chapter-15/recipe-01/cxx-example/build
Start 1: test_short
1/2 Test #1: test_short ....................... Passed 0.00 sec
Start 2: test_long
2/2 Test #2: test_long ........................ Passed 0.00 sec
100% tests passed, 0 tests failed out of 2
Total Test time (real) = 0.01 sec
Performing coverage
Cannot find any coverage files. Ignoring Coverage request.
Submit files (using http)
Using HTTP submit method
Drop site:http://my.cdash.org/submit.php?project=cmake-cookbook
Uploaded: /home/user/cmake-recipes/chapter-14/recipe-01/cxx-example/build/Testing/20180408-1449/Build.xml
Uploaded: /home/user/cmake-recipes/chapter-14/recipe-01/cxx-example/build/Testing/20180408-1449/Configure.xml
Uploaded: /home/user/cmake-recipes/chapter-14/recipe-01/cxx-example/build/Testing/20180408-1449/Test.xml
Submission successful最后,可以在浏览器中看到测试结果(本例中,测试结果上报到 https://my.cdash.org/index.php?project=cmake-cookbook ):
工作原理
可以从更高级的角度展示工作流,CTest运行测试并在XML文件中记录结果。然后,将这些XML文件发送到CDash服务器,在那里可以浏览和分析它们。通过单击数字2
,获得关于通过或失败测试的更多的细节信息(本例中,没有失败的测试)。如下图所示,详细记录了运行测试的机器的信息,以及时间信息。同样,单个测试的测试输出也可以在线浏览。
CTest支持三种不同的提交模式:
- 实验性构建
- 夜间构建
- 持续构建
我们使用了ctest --dashboard Experimental
(实验性构建提交),因此,测试结果显示在实验模式之下。实验模式对于测试代码的当前状态、调试新的仪表板脚本、调试CDash服务器或项目非常有用。夜间构建模式,将把代码更新(或降级)到最接近最近夜间构建开始时的存储库,这些可以在 CTestConfig.cmake
中设置。其为接收更新频繁的项目的所有夜间测试提供一个定义良好的参考。例如,夜间开始时间可以设置为世界时的”午夜”:
1 | set(CTEST_NIGHTLY_START_TIME "00:00:00 UTC") |
持续模式对于集成工作流非常有用,它将把代码更新到最新版本。
TIPS:构建、测试和提交到实验面板只需要一个命令— cmake --build . --target Experimental
更多信息
这个示例中,我们直接从测试目标部署到CDash。我们将在本章后面的第3和第4部分中,使用专用的CTest脚本。
CDash不仅可以监视测试是否通过或失败,还可以看到测试时间。可以为测试计时进行配置:如果测试花费的时间超过分配的时间,它将被标记为失败。这对于基准测试非常有用,可以在重构代码时自动检测性能测试用例的性能情况。
有关CDash定义和配置设置的详细讨论,请参见官方CDash文档,网址为 https://public.kitware.com/Wiki/CDash:Documentation
14.2 CDash显示测试覆盖率
NOTE:此示例代码可以在 https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-14/recipe-02 中找到,其中包含一个C++示例。该示例在CMake 3.5版(或更高版本)中是有效的,并且已经在GNU/Linux、macOS和Windows上进行过测试。
本示例中,我们将测试覆盖率报告给CDash,面板上将能够逐行浏览测试覆盖率分析,以便识别未测试或未使用的代码。
准备工作
我们将扩展前一节的源代码,在src/sum_integers.cpp
中做一个小的修改,添加一个函数sum_integers_unused
:
1 |
|
我们使用gcov(https://gcc.gnu.org/onlinedocs/gcc/Gcov.html )通过覆盖率分析检测这个未使用的代码。
具体实施
通过以下步骤,我们将使用覆盖率分析,并将结果上传到面板:
主
CMakeLists.txt
和tests/CMakeLists.txt
文件与前一个示例相同。我们将扩展
src/CMakeLists.txt
,并提供一个选项来添加用于代码覆盖率的编译标志。此选项默认启用:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17option(ENABLE_COVERAGE "Enable coverage" ON)
if(ENABLE_COVERAGE)
if(CMAKE_CXX_COMPILER_ID MATCHES GNU)
message(STATUS "Coverage analysis with gcov enabled")
target_compile_options(sum_integers
PUBLIC
-fprofile-arcs -ftest-coverage -g
)
target_link_libraries(sum_integers
PUBLIC
gcov
)
else()
message(WARNING "Coverage not supported for this compiler")
endif()
endif()然后,配置、构建,并将结果上传CDash:
1
2
3
4mkdir -p build
cd build
cmake ..
cmake --build . --target Experimental最后一步,执行测试覆盖率分析:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18Performing coverage
Processing coverage (each . represents one file):
...
Accumulating results (each . represents one file):
...
Covered LOC: 14
Not covered LOC: 7
Total LOC: 21
Percentage Coverage: 66.67%
Submit files (using http)
Using HTTP submit method
Drop site:http://my.cdash.org/submit.php?project=cmake-cookbook
Uploaded: /home/user/cmake-recipes/chapter-14/recipe-02/cxx-example/build/Testing/20180408-1530/Build.xml
Uploaded: /home/user/cmake-recipes/chapter-14/recipe-02/cxx-example/build/Testing/20180408-1530/Configure.xml
Uploaded: /home/user/cmake-recipes/chapter-14/recipe-02/cxx-example/build/Testing/20180408-1530/Coverage.xml
Uploaded: /home/user/cmake-recipes/chapter-14/recipe-02/cxx-example/build/Testing/20180408-1530/CoverageLog-0.xml
Uploaded: /home/user/cmake-recipes/chapter-14/recipe-02/cxx-example/build/Testing/20180408-1530/Test.xml
Submission successful最后,可以在浏览器中验证测试结果(本例的测试结果报告在 https://my.cdash.org/index.php?project=cmake-cookbook ):
工作原理
测试覆盖率为66.67%。为了得到更深入的了解,我们可以点击百分比,得到两个子目录的覆盖率分析:
通过浏览子目录链接,我们可以检查单个文件的测试覆盖率,甚至可以逐行浏览摘要(例如,src/sum_integs.cpp
):
运行测试时,绿线部分已经被覆盖,而红线部分则没有。通过这个方法,我们不仅可以标识未使用的/未测试的代码(使用sum_integers_used
函数),还可以查看每一行代码被遍历的频率。例如,代码行sum += i
已经被访问了1005次(在test_short
期间访问了5次,在test_long
期间访问了1000次)。测试覆盖率分析是自动化测试不可或缺的功能,CDash为我们提供了一个界面,可以在浏览器中图形化地浏览分析结果。
更多信息
为了更多的了解该特性,我们推荐读者阅读下面的博客文章,它更深入的讨论了CDash的覆盖特性:https://blog.kitware.com/additional-coverage-features-in-cdash/
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。
准备工作
这个例子中,我们将使用两个源文件和两个测试集:
1 | . |
buggy.cpp
包含有两个bug:
1 |
|
这些函数在相应的头文件中声明(buggy.hpp
):
1 |
|
测试文件leaky.cpp
中将会验证function_leaky
的返回值:
1 |
|
相应地,use_after_free.cpp
会检查function_use_after_free
的返回值:
1 |
|
具体实施
为了使用ASan,我们需要使用特定的标志来编译代码。然后,我们将运行测试并将它们提交到面板。
生成bug库的工作将在
src/CMakeLists.txt
中完成:1
2
3
4
5
6
7
8
9
10
11
12
13add_library(buggy "")
target_sources(buggy
PRIVATE
buggy.cpp
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/buggy.hpp
)
target_include_directories(buggy
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)在文件
src/CMakeLists.txt
中,我们将添加一个选项用于使用ASan:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17option(ENABLE_ASAN "Enable AddressSanitizer" OFF)
if(ENABLE_ASAN)
if(CMAKE_CXX_COMPILER_ID MATCHES GNU)
message(STATUS "AddressSanitizer enabled")
target_compile_options(buggy
PUBLIC
-g -O1 -fsanitize=address -fno-omit-frame-pointer
)
target_link_libraries(buggy
PUBLIC
asan
)
else()
message(WARNING "AddressSanitizer not supported for this compiler")
endif()
endif()测试在
tests/CMakeLists.txt
中定义:1
2
3
4
5
6
7
8
9
10
11foreach(_test IN ITEMS leaky use_after_free)
add_executable(${_test} ${_test}.cpp)
target_link_libraries(${_test} buggy)
add_test(
NAME
${_test}
COMMAND
$<TARGET_FILE:${_test}>
)
endforeach()主
CMakeLists.txt
与之前的示例基本相同:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20# set minimum cmake version
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
# project name and language
project(recipe-03 LANGUAGES CXX)
# require C++11
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# process src/CMakeLists.txt
add_subdirectory(src)
enable_testing()
# allow to report to a cdash dashboard
include(CTest)
# process tests/CMakeLists.txt
add_subdirectory(tests)CTestConfig.cmake
也没有修改:1
2
3
4set(CTEST_DROP_METHOD "http")
set(CTEST_DROP_SITE "my.cdash.org")
set(CTEST_DROP_LOCATION "/submit.php?project=cmake-cookbook")
set(CTEST_DROP_SITE_CDASH TRUE)这个示例中,我们使用CTest脚本向CDash提交结果;为此,我们将创建一个文件
dashboard.cmake
(与主CMakeLists.txt
和`` CTestConfig.cmake`位于同一个目录下):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29set(CTEST_PROJECT_NAME "example")
cmake_host_system_information(RESULT _site QUERY HOSTNAME)
set(CTEST_SITE ${_site})
set(CTEST_BUILD_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_HOST_SYSTEM_PROCESSOR}")
set(CTEST_SOURCE_DIRECTORY "${CTEST_SCRIPT_DIRECTORY}")
set(CTEST_BINARY_DIRECTORY "${CTEST_SCRIPT_DIRECTORY}/build")
include(ProcessorCount)
ProcessorCount(N)
if(NOT N EQUAL 0)
set(CTEST_BUILD_FLAGS -j${N})
set(ctest_test_args ${ctest_test_args} PARALLEL_LEVEL ${N})
endif()
ctest_start(Experimental)
ctest_configure(
OPTIONS
-DENABLE_ASAN:BOOL=ON
)
ctest_build()
ctest_test()
set(CTEST_MEMORYCHECK_TYPE "AddressSanitizer")
ctest_memcheck()
ctest_submit()我们将执行
dashboard.cmake
脚本。注意,我们使用CTEST_CMAKE_GENERATOR
与生成器选项的方式:1
2
3
4
5
6
7
8$ ctest -S dashboard.cmake -D
CTEST_CMAKE_GENERATOR="Unix Makefiles"
Each . represents 1024 bytes of output
. Size of output: 0K
Each symbol represents 1024 bytes of output.
'!' represents an error and '*' a warning.
. Size of output: 1K结果将会出现在CDash网站上:
具体实施
这个示例中,成功地向仪表板的动态分析部分报告了内存错误。我们可以通过浏览缺陷详细信息,得到进一步的了解:
通过单击各个链接,可以浏览完整信息的输出。
注意,也可以在本地生成AddressSanitizer报告。这个例子中,我们需要设置ENABLE_ASAN
:
1 | $ mkdir -p build |
运行leaky
测试,直接产生以下结果:
1 | $ ./build/tests/leaky |
相应地,我们可以直接运行use_after_free
,得到详细的输出:
1 | $ ./build/tests/use_after_free |
如果我们在没有AddressSanitizer的情况下进行测试(默认情况下ENABLE_ASAN
是关闭的),就不会报告错误:
1 | $ mkdir -p build_no_asan |
实际上,泄漏只会浪费内存,而use_after_free
可能会导致未定义行为。调试这些问题的一种方法是使用valgrind (http://valgrind.org )。
与前两个示例相反,我们使用了CTest脚本来配置、构建和测试代码,并将报告提交到面板。要了解此示例的工作原理,请仔细查看 dashboard.cmake
脚本。首先,我们定义项目名称并设置主机报告和构建名称:
1 | set(CTEST_PROJECT_NAME "example") |
我们的例子中,CTEST_BUILD_NAME
的计算结果是Linux-x86_64
。不同的操作系统下,可能会观察到不同的结果。
接下来,我们为源和构建目录指定路径:
1 | set(CTEST_SOURCE_DIRECTORY "${CTEST_SCRIPT_DIRECTORY}") |
我们可以将生成器设置为Unix Makefile
:
1 | set(CTEST_CMAKE_GENERATOR "Unix Makefiles") |
但是,对于更具可移植性的测试脚本,我们更愿意通过命令行提供生成器:
1 | $ ctest -S dashboard.cmake -D CTEST_CMAKE_GENERATOR="Unix Makefiles" |
dashboard.cmake
中的下一个代码片段,将计算出机器上可用的CPU芯数量,并将测试步骤的并行级设置为可用CPU芯数量,以使总测试时间最小化:
1 | include(ProcessorCount) |
接下来,我们开始测试步骤并配置代码,将ENABLE_ASAN
设置为ON
:
1 | ctest_start(Experimental) |
dashboard.cmake
其他命令为映射到构建、测试、内存检查和提交步骤:
1 | ctest_build() |
更多信息
细心的读者会注意到,在链接目标之前,我们没有在系统上搜索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/
14.4 使用ThreadSaniiser向CDash报告数据争用
NOTE:此示例代码可以在 https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-14/recipe-03 中找到,其中包含一个C++示例。该示例在CMake 3.5版(或更高版本)中是有效的,并且已经在GNU/Linux、macOS和Windows上进行过测试。
在这个示例中,我们将重用前一个示例中的方法,但是使用ThreadSanitizer或TSan,结合CTest和CDash,来检查数据竞争,并将它们报告给CDash。ThreadSanitizer的文档可以在网上找到,https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual
准备工作
这个示例中,我们将使用以下示例代码(example.cpp
):
1 |
|
这个示例代码中,我们启动16个线程,每个线程都调用increase
函数。increase
函数休眠1s,然后打印并递增一个整数s
。我们预计此示例代码将显示数据竞争,因为所有线程读取和修改相同的地址,而不需要任何显式同步或协调。换句话说,我们期望在代码末尾打印的最终s
,每次的结果都不同。代码有bug,我们将尝试在ThreadSanitizer的帮助下识别数据竞争。如果不运行ThreadSanitizer,我们可能不会看到代码有任何问题:
1 | $ ./example |
具体实施
文件
CMakeLists.txt
首先定义一个受支持的最低版本、项目名称、受支持的语言。在本例中,定义了C++11标准项目:1
2
3
4
5
6
7cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(recipe-04 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)接下来,找到线程库,定义可执行文件,并将其链接到线程库:
1
2
3
4
5
6
7
8find_package(Threads REQUIRED)
add_executable(example example.cpp)
target_link_libraries(example
PUBLIC
Threads::Threads
)然后,提供编译选项和代码,并链接到ThreadSanitizer:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17option(ENABLE_TSAN "Enable ThreadSanitizer" OFF)
if(ENABLE_TSAN)
if(CMAKE_CXX_COMPILER_ID MATCHES GNU)
message(STATUS "ThreadSanitizer enabled")
target_compile_options(example
PUBLIC
-g -O1 -fsanitize=thread -fno-omit-frame-pointer -fPIC
)
target_link_libraries(example
PUBLIC
tsan
)
else()
message(WARNING "ThreadSanitizer not supported for this compiler")
endif()
endif()最后,编译测试用例:
1
2
3
4
5
6
7
8
9
10
11enable_testing()
# allow to report to a cdash dashboard
include(CTest)
add_test(
NAME
example
COMMAND
$<TARGET_FILE:example>
)CTestConfig.cmake
没有变化:1
2
3
4set(CTEST_DROP_METHOD "http")
set(CTEST_DROP_SITE "my.cdash.org")
set(CTEST_DROP_LOCATION "/submit.php?project=cmake-cookbook")
set(CTEST_DROP_SITE_CDASH TRUE)dashboard.cmake
需要为TSan进行简单修改:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29set(CTEST_PROJECT_NAME "example")
cmake_host_system_information(RESULT _site QUERY HOSTNAME)
set(CTEST_SITE ${_site})
set(CTEST_BUILD_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_HOST_SYSTEM_PROCESSOR}")
set(CTEST_SOURCE_DIRECTORY "${CTEST_SCRIPT_DIRECTORY}")
set(CTEST_BINARY_DIRECTORY "${CTEST_SCRIPT_DIRECTORY}/build")
include(ProcessorCount)
ProcessorCount(N)
if(NOT N EQUAL 0)
set(CTEST_BUILD_FLAGS -j${N})
set(ctest_test_args ${ctest_test_args} PARALLEL_LEVEL ${N})
endif()
ctest_start(Experimental)
ctest_configure(
OPTIONS
-DENABLE_TSAN:BOOL=ON
)
ctest_build()
ctest_test()
set(CTEST_MEMORYCHECK_TYPE "ThreadSanitizer")
ctest_memcheck()
ctest_submit()让我们以这个例子为例。通过
CTEST_CMAKE_GENERATOR
选项来设置生成器:1
2
3
4
5
6
7$ ctest -S dashboard.cmake -D CTEST_CMAKE_GENERATOR="Unix Makefiles"
Each . represents 1024 bytes of output
. Size of output: 0K
Each symbol represents 1024 bytes of output.
'!' represents an error and '*' a warning.
. Size of output: 0K在面板上,我们将看到以下内容:
我们可以看到更详细的动态分析:
工作原理
该示例CMakeLists.txt
的核心部分:
1 | option(ENABLE_TSAN "Enable ThreadSanitizer" OFF) |
dashboard.cmake
也需要更新:
1 | # ... |
和上一个示例一样,我们也可以在本地查看ThreadSanitizer的输出:
1 | $ mkdir -p build |
更多信息
对使用OpenMP的应用TSan是很常见的,但是请注意,在某些情况下,OpenMP会在TSan下生成误检的结果。对于Clang编译器,一个解决方案是用-DLIBOMP_TSAN_SUPPORT=TRUE
重新编译编译器本身及其libomp
。通常,以合理的方式使用TSan可能需要重新编译整个工具堆栈,以避免误报。在使用pybind11的C++项目的情况,我们可能需要重新编译Python,并启用TSan来获得有意义的东西。或者,Python绑定可以通过使用TSan抑制而被排除在外,如 https://github.com/google/sanitizers/wiki/threadsanitizersuppression 。例如:如果一个动态库同时被一个经过TSan的二进制文件和一个Python插件调用,那么这种情况可能是不可能使用TSan。
下面的博客文章讨论了如何添加对动态分析工具的支持:https://blog.kitware.com/ctest-cdash-add-support-for-new-dynamic-analysis-tools/