update static analysis documentation

This commit is contained in:
ttroy50
2015-11-21 23:41:38 +00:00
parent 081fcede89
commit 62dc7344d6
2 changed files with 102 additions and 18 deletions

View File

@@ -1 +1,14 @@
How to call static analysis tools # Static Analysis
Static analysis is the analysis of code without executing it. It can be used to find common programming errors and enforce coding guidelines. Examples of errors that can be found using static analysis tools include:
* Out of bounds errors
* Memory leaks
* Usage of uninitialized variables
* Use of unsafe functions
Analysis tools can detect errors early and are becoming a standard tool in most build chains. Some build tools such as (Clang](http://clang-analyzer.llvm.org/) include a build in static analysis tool. However standalone tools also exist.
The examples here include using the following tools:
* (CppCheck)[http://cppcheck.sourceforge.net/]

View File

@@ -1,6 +1,6 @@
# CppCheck Static Analysis # CppCheck Static Analysis
This example is for calling the cppcheck tool to do static analysis. This example is for calling the (CppCheck)[http://cppcheck.sourceforge.net/] tool to do static analysis.
It shows how to add cppcheck with a target for each sub-projects and also how to generate an overall "make analysis" target to do static analysis on all sub-projects. It shows how to add cppcheck with a target for each sub-projects and also how to generate an overall "make analysis" target to do static analysis on all sub-projects.
@@ -8,7 +8,7 @@ It shows how to add cppcheck with a target for each sub-projects and also how to
To run this example you must have the CppCheck utility installed. On Ubuntu you can install it as To run this example you must have the CppCheck utility installed. On Ubuntu you can install it as
``` ```cmake
$ sudo apt-get install cppcheck $ sudo apt-get install cppcheck
``` ```
@@ -20,13 +20,13 @@ $ sudo apt-get install cppcheck
The cmake/modules/FindCppCheck.cmake file contains the code to find and add variables for a custom package module. Custom modules can be used to find programs, libraries and header files to include in your program. The cmake/modules/FindCppCheck.cmake file contains the code to find and add variables for a custom package module. Custom modules can be used to find programs, libraries and header files to include in your program.
``` ```cmake
find_program(CPPCHECK_BIN NAMES cppcheck) find_program(CPPCHECK_BIN NAMES cppcheck)
``` ```
Search the path for the program "cppcheck" and store the result in the CPPCHECK_BIN variable Search the path for the program "cppcheck" and store the result in the CPPCHECK_BIN variable
``` ```cmake
set (CPPCHECK_THREADS "-j 4" CACHE STRING "The -j argument to have cppcheck use multiple threads / cores") set (CPPCHECK_THREADS "-j 4" CACHE STRING "The -j argument to have cppcheck use multiple threads / cores")
set (CPPCHECK_ARG "${CPPCHECK_THREADS}" CACHE STRING "The arguments to pass to cppcheck. If set will overwrite CPPCHECK_THREADS") set (CPPCHECK_ARG "${CPPCHECK_THREADS}" CACHE STRING "The arguments to pass to cppcheck. If set will overwrite CPPCHECK_THREADS")
@@ -34,7 +34,7 @@ set (CPPCHECK_ARG "${CPPCHECK_THREADS}" CACHE STRING "The arguments to pass to c
Set some custom arguments that can be later passed to cppcheck. Set some custom arguments that can be later passed to cppcheck.
``` ```cmake
include(FindPackageHandleStandardArgs) include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS( FIND_PACKAGE_HANDLE_STANDARD_ARGS(
CPPCHECK CPPCHECK
@@ -54,7 +54,7 @@ Export the variables so that they can be seen from ccmake / cmake-gui and set in
#### Setting path to custom modules #### Setting path to custom modules
``` ```cmake
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules
${CMAKE_MODULE_PATH}) ${CMAKE_MODULE_PATH})
``` ```
@@ -63,7 +63,7 @@ The ${CMAKE_MODULE_PATH} points towards a folder which contains custom cmake mod
To then add the package module you can call To then add the package module you can call
``` ```cmake
find_package(CppCheck) find_package(CppCheck)
``` ```
@@ -71,7 +71,7 @@ find_package(CppCheck)
The scope of variables when they are declared / changed is typically in the function of file the are called. To make a change to a variable which is the caller of your scope, you should call it as follows: The scope of variables when they are declared / changed is typically in the function of file the are called. To make a change to a variable which is the caller of your scope, you should call it as follows:
``` ```cmake
set(ALL_ANALYSIS_TARGETS "${ALL_ANALYSIS_TARGETS}" PARENT_SCOPE) set(ALL_ANALYSIS_TARGETS "${ALL_ANALYSIS_TARGETS}" PARENT_SCOPE)
``` ```
@@ -79,7 +79,7 @@ set(ALL_ANALYSIS_TARGETS "${ALL_ANALYSIS_TARGETS}" PARENT_SCOPE)
The add_analysis macro in cmake/analysis.cmake is the core idea for this example. If cppcheck is available then a list of arguments are compiled and added to a custom command that calls cppcheck on the sources. These are then added to a custom target. The add_analysis macro in cmake/analysis.cmake is the core idea for this example. If cppcheck is available then a list of arguments are compiled and added to a custom command that calls cppcheck on the sources. These are then added to a custom target.
``` ```cmake
get_property(dirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES) get_property(dirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES)
foreach(dir ${dirs}) foreach(dir ${dirs})
LIST(APPEND cppcheck_includes "-I${dir}") LIST(APPEND cppcheck_includes "-I${dir}")
@@ -88,14 +88,14 @@ endforeach()
Find the include files from and calls to include_directories() in the same project. Find the include files from and calls to include_directories() in the same project.
``` ```cmake
LIST(APPEND ALL_ANALYSIS_TARGETS "${_target}_analysis") LIST(APPEND ALL_ANALYSIS_TARGETS "${_target}_analysis")
set(ALL_ANALYSIS_TARGETS "${ALL_ANALYSIS_TARGETS}" PARENT_SCOPE) set(ALL_ANALYSIS_TARGETS "${ALL_ANALYSIS_TARGETS}" PARENT_SCOPE)
``` ```
Export the target name into a variable that can later be used to add a global "make analysis" target. Export the target name into a variable that can later be used to add a global "make analysis" target.
``` ```cmake
if (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VESION} GREATER 2.7) if (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VESION} GREATER 2.7)
separate_arguments(tmp_args UNIX_COMMAND ${CPPCHECK_ARG}) separate_arguments(tmp_args UNIX_COMMAND ${CPPCHECK_ARG})
else () else ()
@@ -106,13 +106,14 @@ endif ()
Change the CPPCHECK_ARG so that the can be added to command correctly in the custom command. Change the CPPCHECK_ARG so that the can be added to command correctly in the custom command.
``` ```cmake
add_custom_target(${_target}_analysis) add_custom_target(${_target}_analysis)
set_target_properties(${_target}_analysis PROPERTIES EXCLUDE_FROM_ALL TRUE) set_target_properties(${_target}_analysis PROPERTIES EXCLUDE_FROM_ALL TRUE)
``` ```
Add a custom target with a name you have passed in followed by _analysis. Do not include this in the all target. Add a custom target with a name you have passed in followed by _analysis. Do not include this in the all target.
```
```cmake
add_custom_command(TARGET ${_target}_analysis PRE_BUILD add_custom_command(TARGET ${_target}_analysis PRE_BUILD
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMAND ${CPPCHECK_BIN} ${tmp_args} ${cppcheck_includes} ${${_sources}} COMMAND ${CPPCHECK_BIN} ${tmp_args} ${cppcheck_includes} ${${_sources}}
@@ -125,7 +126,7 @@ Add a custom command which is called from the custom target added above. This wi
To call the add_analysis marco add the following to your projects CMakeLists.txt file: To call the add_analysis marco add the following to your projects CMakeLists.txt file:
``` ```cmake
include(${CMAKE_SOURCE_DIR}/cmake/analysis.cmake) include(${CMAKE_SOURCE_DIR}/cmake/analysis.cmake)
add_analysis(${PROJECT_NAME} SOURCES) add_analysis(${PROJECT_NAME} SOURCES)
``` ```
@@ -138,13 +139,13 @@ To achieve this 2 things should be added to the root CMakeLists.txt
First add an empty variable ALL_ANALYSIS_TARGETS before calling your add_subdirectories() function. First add an empty variable ALL_ANALYSIS_TARGETS before calling your add_subdirectories() function.
``` ```cmake
set (ALL_ANALYSIS_TARGETS) set (ALL_ANALYSIS_TARGETS)
``` ```
Second add the following after your add_subdirectories() call. Second add the following after your add_subdirectories() call.
``` ```cmake
if( CPPCHECK_FOUND ) if( CPPCHECK_FOUND )
add_custom_target(analysis) add_custom_target(analysis)
ADD_DEPENDENCIES(analysis ${ALL_ANALYSIS_TARGETS}) ADD_DEPENDENCIES(analysis ${ALL_ANALYSIS_TARGETS})
@@ -154,6 +155,76 @@ endif()
``` ```
This adds the "make analysis" target which calls all the sub-targets. This adds the "make analysis" target which calls all the sub-targets.
## Building the example
```bash
$ mkdir build
$ cd build/
$ cmake ..
-- The C compiler identification is GNU 4.8.4
-- The CXX compiler identification is GNU 4.8.4
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Found CPPCHECK: /usr/bin/cppcheck
adding cppcheck analysys target for subproject1
adding cppcheck analysys target for subproject2
analysis analysis targets are subproject1_analysis;subproject2_analysis
-- Configuring done
-- Generating done
-- Build files have been written to: /home/matrim/workspace/cmake-examples/04-static-analysis/cppcheck/build
$ make analysis
Scanning dependencies of target subproject1_analysis
Running cppcheck: subproject1
Checking main1.cpp...
Built target subproject1_analysis
Scanning dependencies of target subproject2_analysis
Running cppcheck: subproject2
Checking main2.cpp...
[main2.cpp:7]: (error) Array 'tmp[10]' accessed at index 11, which is out of bounds.
Built target subproject2_analysis
Scanning dependencies of target analysis
Built target analysis
```
The above calls cppcheck in both subproject folders as
```bash
...
cd /path/to/subproject1 && /usr/bin/cppcheck -j 4 main1.cpp
...
cd /path/to/subproject2 && /usr/bin/cppcheck -j 4 main2.cpp
...
```
The main1.cpp has no errors so will complete correctly, however the main2.cpp includes an out-of-bounds error which shows the error.
```
[main2.cpp:7]: (error) Array 'tmp[10]' accessed at index 11, which is out of bounds.
```
By default cppcheck only prints warnings and exits with a successful exit code. If the ${CPPCHECK_ARG} variable is set with "--error-exitcode=1", the make analysis will finish early as follows.
```
$ make analysis
Running cppcheck: subproject2
Checking main2.cpp...
[main2.cpp:7]: (error) Array 'tmp[10]' accessed at index 11, which is out of bounds.
make[3]: *** [subproject2_analysis] Error 1
make[2]: *** [subproject2/CMakeFiles/subproject2_analysis.dir/all] Error 2
make[1]: *** [CMakeFiles/analysis.dir/rule] Error 2
make: *** [analysis] Error 2
```
## Extra Notes ## Extra Notes
@@ -175,6 +246,6 @@ If you have a multiple folders levels, where one folder just points to sub folde
You must add the following to the src/CMakeLists.txt file to correctly generate the "make analysis" target You must add the following to the src/CMakeLists.txt file to correctly generate the "make analysis" target
``` ```cmake
set(analysis_TARGETS "${analysis_TARGETS}" PARENT_SCOPE) set(analysis_TARGETS "${analysis_TARGETS}" PARENT_SCOPE)
``` ```