Clang static analysis

by 3 contributors:

Clang has a built-in static analyzer. The gist is you add a wrapper around the compiler which directs all static analysis results to a common location. At the end of a build, these reports are aggregated into a report document showing all the potential issues.

These instructions will only work where Mozilla already compiles with Clang. At the time this was written, Mac OS X has strong support. Linux should work, but isn't well tested.

Ideally, the static analysis would be performed independently from compilation. See bug 663442 to track a work around that facilitates easier static analysis.

Installing Clang

The first step to running static analysis is installing Clang. Clang is rapidly changing, so it is recommended that you use the latest version you can get your hands on, including the latest development source. At the time this was written, the Clang shipping with Xcode on OS X does not have all the tools necessary. You will need to install Clang yourself.

See the Clang Getting Started documentation for the full instructions. But here's a quick look at what you need to do.  Make sure that you have Ninja installed.

# Obtain the source code and set up Git
$ git clone http://llvm.org/git/llvm.git
$ cd llvm/tools
$ git clone http://llvm.org/git/clang.git
$ cd clang
$ git config branch.master.rebase true
$ cd ../..
$ git config branch.master.rebase true
# You should be in the llvm parent directory

# Prepare your object directory. This is where all your compiled files go (similar to Mozilla object dirs)
$ mkdir obj
$ cd obj

# Configure with optimized builds set to install in /usr/local/llvm
# We build optimized because it is a last faster. You can use any --with-prefix
# path, but the following instructions assume /usr/local/llvm.
$ cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local/llvm
$ ninja install

Configuring the build environment

Once you have your Clang build in place, you'll need to set up tools to use it.

First, ensure that the default clang and clang++ binaries are the ones you just installed:

$ export PATH=/usr/local/llvm/bin:$PATH
$ which clang
/usr/local/llvm/bin/clang

Next, tell the Mozilla build environment to use the Clang static analyzer wrappers for compiling. In your .mozconfig, add the following:

CC=/path/to/llvm/tools/clang/tools/scan-build/ccc-analyzer
CXX=path/to/llvm/tools/clang/tools/scan-build/c++-analyzer

Please note that the location of these tools should be the full path to Clang source code you obtained earlier.

A full working .mozconfig for the desktop browser is:

CC=/path/to/llvm/tools/clang/tools/scan-build/ccc-analyzer
CXX=/path/to/llvm/tools/clang/tools/scan-build/c++-analyzer

. $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 $clang_source/tools/scan-build/scan-build.

Try running your build through scan-build:

$ cd /path/to/mozilla/source

# Blow away your object directory because incremental builds don't make sense
$ rm -rf obj-dir

$ /path/to/llvm/tools/clang/tools/scan-build/scan-build --use-analyzer /usr/local/bin/clang ./mach build

# 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.

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 $clang_source/tools/scan-build/scan-build.

Try running your build through scan-build:

$ cd obj-ff-dbg
$ /Users/gps/src/llvm/tools/clang/tools/scan-build/scan-build make -j6 --no-print-directory

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.

By default, 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 scan-build.

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-view (from $clang_source/tools/scan-view/scan-view) as 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.

False positives

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.

See also

Document Tags and Contributors

Contributors to this page: Ehsan, Sheppy, gps
Last updated by: Ehsan,