6. Static and runtime analysis#
Beyond unit testing, Pigweed provides well-lit paths for integrating a variety of tools for Static and runtime analysis.
In this section we’ll show you how to run some of these tools for Sense, and discuss actual issues that we caught by using them.
UndefinedBehaviorSanitizer#
UndefinedBehaviorSanitizer, or ubsan, is a fast undefined behavior detector.
In general, tools like ubsan are unlikely to run on-device. But as you just learned in 5. Run host tests, all unit tests in Sense can run on your host! So, we can rebuild these tests with ubsan instrumentation and run them via:
bazelisk test --config=ubsan //...
Example issue#
We used ubsan to catch a real issue when developing Sense: b/385400496. We
originally initialized an std::chrono::time_point class data member
to std::chrono::time_point::min()
. This looks like a reasonable thing to
do, but it turns out std::chrono::time_point::min()
corresponds to the
earliest time representable by the underlying type (sometime on September 21,
1677). When you try to compute the time difference between the current time and
this initial time in the 17th century, you get signed integer
overflow—undefined behavior in C++!
This bug nicely illustrates the value of ubsan:
Although the test is executed on the host, it caught a bug that was also present in the embedded code.
The bug would be very difficult to identify by inspecting the code. It’s caused by an implementation detail (the numerical type used to represent time points) that
std::chrono
attempts, but fails, to abstract.
AddressSanitizer#
AddressSanitizer, or asan, detects memory errors such as out-of-bounds access and use-after-free.
Similarly to ubsan, you can rebuild and rerun the tests with asan instrumentation via:
bazelisk test --config=asan //...
Example issue#
AddressSanitizer identified a lifetime issue in a multithreaded test (b/352163490): some objects referenced by pubsub subscribers failed to outlive the pubsub.
In this case, we didn’t need asan to detect there was an issue. The test failed in the regular build configuration, too, but with cryptic test assertion errors. AddressSanitizer pin-pointed that the bug was accessing memory after it had been freed, and provided stack traces of the incorrect access.
Fixing this required restructuring the test to ensure all objects outlived their references.
clang-tidy#
clang-tidy is a C++ “linter” and
static analysis tool. It identifies bug-prone patterns (e.g., use after move),
non-idiomatic usage (e.g., creating std::unique_ptr
with new
rather than
std::make_unique
), and performance issues (e.g., unnecessary copies of loop
variables).
You can run clang-tidy on all C++ build targets in Sense by running:
bazelisk build --config=clang-tidy //...
Note
It’s bazelisk build
, not bazelisk test
, because clang-tidy does not
require executing the code. It’s a static check performed at build time.
Example issues#
When we enabled clang-tidy in Sense in http://pwrev.dev/266915, we found a number of pre-existing issues:
Missing override keywords A function or destructor marked
override
that is not an override of a base class virtual function will not compile, and this helps catch common errors. Not using this keyword on methods which are overrides prevents the compiler from helping you!Mismatched parameter names in definitions and declarations. This hinders readability: the same parameter is referred to by a different name in different parts of the code!
static
functions in headers at namespace scope. (This is an error because astatic
function in a header generates a copy in every translation unit it is used in. Such functions should be markedinline
instead ofstatic
.)
Pylint#
Pylint is a customizable Python linter that detects problems such as overly
broad catch
statements, unused arguments/variables, and mutable default
parameter values.
You can run it on the Python targets in Sense by running:
bazelisk build --config=pylint //...
Example issues#
We enabled Pylint for Sense in http://pwrev.dev/309782. We found no real bugs, but many missing docstrings, too-long-lines, unused imports, and unused variables.
Layering check#
Pigweed toolchains have support for layering check. The layering
check makes it a compile-time error to #include
a header that’s not in the
hdrs
of a cc_library
you directly depend on. This produces cleaner
dependency graphs and makes future code refactorings easier. See
Layering check for more details.
There’s no separate command for running the layering check: it’s always enforced when building the code.
Example issues#
We enabled layering check for Sense in http://pwrev.dev/261652. As you can see, even in this relatively small project we accumulated a large number of dependencies that were not properly reflected in the build graph!
Summary#
Now it’s time for the fun stuff. Head over to 7. Run the host app
to try out the bringup app, blinky
.