# 4.5 使用动态分析来检测内存缺陷

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

内存缺陷：写入或读取越界，或者内存泄漏(已分配但从未释放的内存)，会产生难以跟踪的bug，最好尽早将它们检查出来。Valgrind( <http://valgrind.org> )是一个通用的工具，用来检测内存缺陷和内存泄漏。本节中，我们将在使用CMake/CTest测试时使用Valgrind对内存问题进行警告。

## 准备工作

对于这个配置，需要三个文件。第一个是测试的实现(我们可以调用文件`leaky_implementation.cpp`):

```cpp
#include "leaky_implementation.hpp"

int do_some_work() {

  // we allocate an array
  double *my_array = new double[1000];

  // do some work
  // ...

  // we forget to deallocate it
  // delete[] my_array;

  return 0;
}
```

还需要相应的头文件(`leaky_implementation.hpp`):

```cpp
#pragma once

int do_some_work();
```

并且，需要测试文件(`test.cpp`):

```cpp
#include "leaky_implementation.hpp"

int main() {
  int return_code = do_some_work();

  return return_code;
}
```

我们希望测试通过，因为`return_code`硬编码为`0`。这里我们也期望检测到内存泄漏，因为`my_array`没有释放。

## 具体实施

下面展示了如何设置CMakeLists.txt来执行代码动态分析:

1. 我们首先定义CMake最低版本、项目名称、语言、目标和依赖关系:

   ```
   cmake_minimum_required(VERSION 3.5 FATAL_ERROR)

   project(recipe-05 LANGUAGES CXX)

   set(CMAKE_CXX_STANDARD 11)
   set(CMAKE_CXX_EXTENSIONS OFF)
   set(CMAKE_CXX_STANDARD_REQUIRED ON)

   add_library(example_library leaky_implementation.cpp)

   add_executable(cpp_test test.cpp)
   target_link_libraries(cpp_test example_library)
   ```
2. 然后，定义测试目标，还定义了`MEMORYCHECK_COMMAND`:

   ```
   find_program(MEMORYCHECK_COMMAND NAMES valgrind)
   set(MEMORYCHECK_COMMAND_OPTIONS "--trace-children=yes --leak-check=full")

   # add memcheck test action
   include(CTest)

   enable_testing()

   add_test(
     NAME cpp_test
     COMMAND $<TARGET_FILE:cpp_test>
     )
   ```
3. 运行测试集，报告测试通过情况，如下所示:

   ```
   $ ctest

   Test project /home/user/cmake-recipes/chapter-04/recipe-05/cxx-example/build
   Start 1: cpp_test
   1/1 Test #1: cpp_test ......................... Passed 0.00 sec
   100% tests passed, 0 tests failed out of 1
   Total Test time (real) = 0.00 sec
   ```
4. 现在，我们希望检查内存缺陷，可以观察到被检测到的内存泄漏:

   ```
   $ ctest -T memcheck

   Site: myhost
   Build name: Linux-c++
   Create new tag: 20171127-1717 - Experimental
   Memory check project /home/user/cmake-recipes/chapter-04/recipe-05/cxx-example/build
   Start 1: cpp_test
   1/1 MemCheck #1: cpp_test ......................... Passed 0.40 sec
   100% tests passed, 0 tests failed out of 1
   Total Test time (real) = 0.40 sec
   -- Processing memory checking output:
   1/1 MemCheck: #1: cpp_test ......................... Defects: 1
   MemCheck log files can be found here: ( * corresponds to test number)
   /home/user/cmake-recipes/chapter-04/recipe-05/cxx-example/build/Testing/Temporary/MemoryChecker.*.log
   Memory checking results:
   Memory Leak - 1
   ```
5. 最后一步，应该尝试修复内存泄漏，并验证`ctest -T memcheck`没有报告错误。

## 工作原理

使用`find_program(MEMORYCHECK_COMMAND NAMES valgrind)`查找valgrind，并将`MEMORYCHECK_COMMAND`设置为其绝对路径。我们显式地包含CTest模块来启用`memcheck`测试操作，可以使用`CTest -T memcheck`来启用这个操作。此外，使用`set(MEMORYCHECK_COMMAND_OPTIONS "--trace-children=yes --leak-check=full")`，将相关参数传递给Valgrind。内存检查会创建一个日志文件，该文件可用于详细记录内存缺陷信息。

**NOTE**:*一些工具，如代码覆盖率和静态分析工具，可以进行类似地设置。然而，其中一些工具的使用更加复杂，因为需要专门的构建和工具链。Sanitizers就是这样一个例子。有关更多信息，请参见*<https://github.com/arsenm/sanitizers-cmake> *。另外，请参阅第14章，其中讨论了AddressSanitizer和ThreadSanitizer。*

## 更多信息

该方法可向测试面板报告内存缺陷，这里演示的功能也可以独立于测试面板使用。我们将在第14章中重新讨论，与CDash一起使用的情况。

有关Valgrind及其特性和选项的文档，请参见<http://valgrind.org> 。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://chenxiaowei.gitbook.io/cmake-cookbook/4.0-chinese/4.5-chinese.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
