staticinit is a Clang LibTooling-based utility that helps identify C++ objects that may participate in the Static Initialization Order Fiasco (SIOF).
The tool analyzes source files using a compile_commands.json compilation database and reports:
- global variables with dynamic initialization;
- static data members with dynamic initialization;
- objects initialized through a non-trivial default constructor;
- references to other global variables or static data members found in initialization expressions;
- whether the referenced variable is dynamically initialized or statically initialized;
- whether a dynamically initialized dependency is defined earlier or later in the same translation unit.
- Uses the project's compilation database (compile_commands.json).
- Analyzes a single source file or an entire project.
- Parallel processing using multiple worker threads.
- Colored output by default.
- Directory exclusion support.
- Detection of potential initialization-order dependencies.
Install the required packages:
sudo apt update
sudo apt install \
clang-18 \
clang-tools-18 \
llvm-18-dev \
libclang-18-dev \
libclang-cpp18-dev \
cmake \
ninja-buildcmake -S . -B build
cmake --build buildThe resulting executable is:
build/staticinit
In your project source directory:
cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ONThe compilation database should be generated as:
build/compile_commands.json
Still in your project source directory:
./build/staticinit -p build./build/staticinit -p build src/foo.cpp./build/staticinit -p build src/foo.cpp src/bar.cppUse all available hardware threads (default):
./build/staticinit -p buildSpecify the number of worker threads:
./build/staticinit -p build -j 2./build/staticinit -p build -i third_party -i generatedAll source files and declarations originating from these directories (and their subdirectories) will be ignored.
Colored output is enabled by default.
./build/staticinit p build -nDefault colors:
- blue: analyzed variable name;
- magenta: initialization expression;
- yellow: legitimate references to other global/static variables.
- red: spurious references to other global/static variables.
Source code:
std::string a;
std::string b = a + " world";
std::string c = d;
std::string d;Output:
foo.cpp:1:13: global: a: std::string
init: <default constructor>
foo.cpp:3:13: global: b: std::string
init: a + " world"
references global/static:
- a: std::string [same TU, before]
foo.cpp:5:13: global: c: std::string
init: d
references global/static:
- d: std::string [same TU, after]
foo.cpp:7:13: global: d: std::string
init: <default constructor>
init: <default constructor>
Indicates that no explicit initializer is present, but a non-trivial constructor will be executed before main().
[same TU, before]
The referenced variable is defined earlier in the same translation unit.
[same TU, before]
The referenced variable has a constant(static) initialization
[same TU, after]
The referenced variable is defined later in the same translation unit.
This may indicate an initialization-order problem.
[not detected as dynamic init in this TU]
The referenced variable is either:
- defined in another translation unit;
- not detected as dynamically initialized;
- filtered out by the tool.
This situation generally deserves manual inspection.
The tool detects dependencies directly visible in initialization expressions.
Example:
std::string b = a + "x";The dependency on a is detected.
However:
std::string b = MakeString();If MakeString() accesses a global variable internally, the dependency is not detected.
The current implementation focuses on dependencies explicitly visible in the AST of initialization expressions.
- Investigating startup crashes occurring before
main(). - Finding potential Static Initialization Order Fiasco issues.
- Auditing large legacy codebases.
- Reducing startup-time complexity.
- Preparing code for migration to constexpr, constinit, or function-local statics.
- Reviewing global state introduced by new code.
Christophe Meynard
SPDX-License-Identifier: GPL-2.0-only