Static Code Analysis in C++
Preamble
This is a blog post I wrote back at CCP to promote better software engineering practices. I’ve decided to publish it here as well in case someone might find it useful.
About Static Code Analysis
As with any engineering discipline then it is important to collect and understand different metrics about the project you are working on. In the case of software engineering then static code analysis is the step where your code is inspected for warnings/errors, compliance with style guides and, last but not least, generation of said metrics happens. Knowing how much code coverage you achieve with your tests is a great indicator for trusting changes you make, spotting dead code automatically helps you avoid confusion when reading through your source and keeping that codebase small. In this blog you are going to learn about how to effectively use the Cppcheck tool to spot potential problems in your source code. For the next installments I plan to talk about how to gain code coverage metrics for your source code, and how to integrate the various tools into Sonar, which is the web frontend that a lot of people at CCP use for visualization of static analysis results.
What is Cppcheck?
To quote the project’s website then:
“Cppcheck is a static analysis tool for C/C++ code. Unlike C/C++ compilers and many other analysis tools it does not detect syntax errors in the code. Cppcheck primarily detects the types of bugs that the compilers normally do not detect”.
The tool itself comes in two flavours: as a command-line utility for integrating it with automated processes, and as a graphical frontend if you want to just toy around with it a bit. Differences between the two are only in terms of usability. The GUI version allows you to save project files for your source code where you can specify your desired analysis configuration. Unfortunately these project files are not available for the command-line version, specifying options there is a bit more tedious, but so far that is the only minor usability inconvenience I’ve discovered.
We’ve picked Cppcheck in Team Special Circumstances because it’s free and supported by the Sonar C++ plugins. There are, however, other tools out there which you might want to give a try. And never forget: Your compiler has a lot of warning levels, and the strictest one is what you usually want to use.
Using Cppcheck from the command line
The most interesting use case for Cppcheck clearly is the integration into
your CI loop. In order to do so you will want to invoke the command-line
utlity, which can be as easy as running the following command: cppcheck <path>
This will recursively discover source in path and run an analysis on what
it finds. It’s going to be quite spammy as the tool will output a lot of
progress information, and thus if you want to omit this kind of noise
simply pass on the --quiet
(or -q
) parameter. This will instruct cppcheck to only print error messages:
cppcheck -q <path>
Now, you most likely do not have all the source code in one location, most
commonly because you are using some external SDK. By default Cppcheck will
report header files it cannot find as an error, so you might want to
specify the -I <dir>
parameter for pointing at a location where these
included files might be found. However, since this is rarely just a single
location which contains these files then this way of passing in extra
locations for included files can be cumbersome. But worry not, the
--includes-file=<file>
command line switch solves this problem. Simply
create a text file where each line contains an include file location and
pass on to Cppcheck like this:
cppcheck -q --includes-file=extraincludes.txt <path>
Note that Cppcheck does not pick up environment variables that specify
include file paths, so you need to specify them in the text file or via the
-I
switch.
You can also enable different checks to perform using the --enable
switch.
Usually I’m simply going for all checks available:
cppcheck --enable=all
However, you can go and enable it for different categories like style, performance or portability. A full list is available from the command line help.
An interesting switch to use is the --inconclusive
switch. This enables
some extra checks where Cppcheck thinks that a problem might exist, but
can’t tell with absolute certainity. Since this can produce false positives
you will want to investigate each of these instances.
What you most likely want to do is to redirect the results from the analysis into a file in order to process it with an external tool. This is what we in Team Special Circumstances use:
cppcheck --includes-file=cppcheck.includes -rp --xml --enable=all ./src 2>cppcheck-result.xml
In this case we enable all checks available, export the output via
the --xml
switch to stderr
and then catch all stderr
output in a file
called cppcheck-result.xml
. The -rp
switch simply says that relative paths
should be used for the output format, and the --includes-file
points to the
sdk paths we are using.
There is a bunch of other switches to specify which language standard to check for, which checks to perform, etc which I will not cover here, mostly because I didn’t need to use them myself (yet). If you are curious about those simply run cppcheck without any parameters and read the documentation Improving Cppcheck performance In general Cppcheck is reasonably fast, though of course it’s runtime depends a lot on the complexity of your codebase. Thus you might want to know how you can fine tune Cppcheck to increase performance.
The simplest method is to go for the -j <jobs>
switch. It instructs to run
<jobs>
threads in parallel when checking, but comes at the expense of
disabling the unusedFunction
check. I’ve also experienced that occasionally
the GUI version locks up and does not finish analysis when using the -j
parameter, but have not yet experienced issues with -j
in the command line
version.
Another time consuming process is Cppcheck’s automatic evaluation of
#ifdef
statements. The best option is to set as many defines as required
via the -D<define>
switch, as this will limit Cppcheck to only evaluate the
defines specified. If it’s easier to just specify some defines not to check
then use the -U<define>
switch. Alternately use the --max-configs=<limit>
switch to tell Cppcheck to only check the first <limit>
configurations it
encounters.
Last but not least, we in Team Special Circumstances run the analysis nightly via our Jenkins server, so we don’t really care if it takes a relatively long time. We’re perfectly happy with an automated process spending some time every night if it saves us long hours of trying to spot a subtle bug in the codebase.