This document is split in two parts. The first part will focus on the modern and robust way of static-analysis and the second part will present the build-time static-analysis.
Clang-Tidy static analysis
Our current static-analysis infrastruture is based on clang-tidy. It uses checkers in order to assert different programming errors present in the code. The checkers that we use are split in 3 categories:
- Mozilla specific checkers. They detect incorrect Gecko programming patterns which could lead to bugs or security issues.
- Clang-tidy checkers. They aim to suggest better programming practices and to improve memory efficiency and performance.
- Clang-analyzer checkers. These checks are more advanced, for example some of them can detect dead code or memory leaks, but as a typical side effect they have false positives. Because of that, we have disabled them for now, but will enable some of them in the near future.
In order to simplify the process of static-analysis we've focused on integrating this process with MozReview and mach. A list with the default checkers that are used during automated scan can be found here.
MozReview static analysis
We created a TaskCluster bot that runs clang static analysis on every patch submitted to MozReview. It then quickly reports any code defects directly on MozReview, thus preventing bad patches from landing until all their defects are fixed. Currently, its feedback is posted in about 10 minutes after a patch series is published on MozReview.
An example of automated review can be found here.
Mach static analysis
It is supported on all Mozilla built platforms. During the first run it automatically installs all of it's dependency like clang-tidy executable in the .mozbuild folder thus making it very easy to use. The resources that are used are provided by toolchain artifacts clang-tidy target.
This is used through
mach static-analysis command that has the following parameters:
check- Runs the checks using the installed helper tool from ~/.mozbuild.
--checks, -c- Checks to enabled during the scan. The checks enabled here are used by default.
--fix, -f- Try to autofix errors detected by the checkers.
--header-filter, -h-f- Regular expression matching the names of the headers to output diagnostic from.Diagnostic from the main file of each translation unit are always displayed.
As an example we to run static-analysis through mach on
google-readability-braces-around-statements check and autofix we would have:
./mach static-analysis check --checks="-*, google-readability-braces-around-statements" --fix dom/presentation/Presentation.cpp
If you want to use a custom clang-tidy binary this can be done by using the
install subcommand of
mach static-analysis, but please note that the archive that is going to be used must be compatible with the directory structure clang-tidy from toolchain artifacts.
./mach static-analysis install clang.tar.gz
Great news: If you want to build with the Mozilla Clang plug-in (located in
/build/clang-plugin and associated with
MOZ_CLANG_PLUGIN and the attributes in
/mfbt/Attributes.h), it's much easier than this: just add
--enable-clang-plugin to your mozconfig!
These instructions will only work where Mozilla already compiles with Clang.
Ideally, the static analysis would be performed independently from compilation. See bug 663442 to track a work around that facilitates easier static analysis.
The first step to running static analysis is installing Clang. Clang is now shipped on most of the GNU/Linux distributions but also shipped with Xcode as the default compiler.
Configuring the build environment
Once you have your Clang build in place, you'll need to set up tools to use it.
A full working .mozconfig for the desktop browser is:
. $topsrcdir/browser/config/mozconfig mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-ff-dbg ac_add_options --enable-debug
Attempts to use
ccache will likely result in failure to compile. It is also necessary to avoid optimized builds, as these will modify macros which will result in many false positives.
At this point, your Mozilla build environment should be configured to compile via the Clang static analyzer!
Performing scanning builds
It is not enough to simply start the build like normal. Instead, you need to run the build through a Clang utility script which will keep track of all produced analysis and consolidate it automatically.
That script is scan-build. You can find it in
Try running your build through
$ cd /path/to/mozilla/source # Blow away your object directory because incremental builds don't make sense $ rm -rf obj-dir # To start the build: scan-build --show-description ./mach build -v # The above should execute without any errors. However, it should take longer than # normal because all compilation will be executing through Clang's static analyzer, # which adds overhead.
If things are working properly, you should see a bunch of console spew, just like any build.
The first time you run scan-build, CTRL+C after a few files are compiled. You should see output like:
scan-build: 3 bugs found. scan-build: Run 'scan-view /Users/gps/tmp/mcsb/2011-12-15-3' to examine bug reports.
If you see a message like:
scan-build: Removing directory '/var/folders/s2/zc78dpsx2rz6cpc_21r9g5hr0000gn/T/scan-build-2011-12-15-1' because it contains no reports.
either no static analysis results were available yet or your environment is not configured properly.
scan-build writes results to a folder in a pseudo-temporary location. You can control where results go by passing the
-o /path/to/output arguments to
You may also want to run
scan-build --help to see all the options available. It is possible to selectively enable and disable individual analyzers, for example.
Analyzing the output
Once the build has completed,
scan-build will produce a report summarizing all the findings. This is called
index.html in the output directory. You can run
scan-build's output suggests; this merely fires up a local HTTP server. Or you should be able to open the
index.html directly with your browser.
There are currently many false positives in the static analyzer. A lot of these are due to the analyzer having difficulties following the relatively complicated error handling in various preprocessor macros. For example, most of our
ASSERT() macros call other functions which themselves call
assert() or do something else.
In the long term, we should add a set of macros enabled via
#ifdef which provide simple macros understandable by the static analyzer. There are also some
pragmas and compiler extensions we can investigate using to silence warnings.