8.1.16. Race Condition Analysis¶
8.1.16.1. Background¶
You have a C/C++ project and want to check if your code has race condition issues. The race condition stylechecks can be used to detect unguarded accesses to global variables from different tasks (data races). In order to get precise results, it is crucial that configuration is done correctly.
8.1.16.2. What can be configured?¶
Useful things to consider:
Entry points for iranalysis and for the race condition check
Partitions and priorities
8.1.16.3. What needs to be done?¶
The checks can optionally use the results of control and data-flow analysis provided by the
StaticSemanticAnalysis rule. This is recommended to consider accesses to all global
variable, even via pointer dereference.
Configure appropriate
EntryPointsto include all tasks and ISRs.Enable and configure the rule
StaticSemanticAnalysis(optional).Activate the stylecheck rule
Parallelism-UnsafeVarAccess.Enable the option
Parallelism-UnsafeVarAccess/inspect_pointersto use the results ofStaticSemanticAnalysis(optional).Configure the
partitionsoption in a Python layer. Check the option description shown inaxivion_configfor detailed requirements.Check that the rule works on the command line by setting the variable
BAUHAUS_CONFIGto your project configuration and calling the toolaxivion_analysis --ir your.irfrom an Axivion Command Prompt. Issues will be output to the shell window.Run the CI analysis and check the results in the dashboard.
To analyze the data race reports in depth, gravis can be used to
produce graphics showing the call graphs contributing to a data race. To
produce these graphics for all data race reports, please download the batch file
make_race_pdfs.bat
on a Windows system or
make_race_pdfs.sh
for GNU/Linux systems.
Alternatively, follow these steps:
In an Axivion Command Prompt, point the variable
BAUHAUS_CONFIGto your project configuration.Create another configuration layer on top of your project configuration, usually by adding a new file
enable_id_output.jsonto theBAUHAUS_CONFIGpath. In that new layer, enable the optionshow_object_numberof the ruleParallelism-UnsafeVarAccess.Run
axivion_analysis --rule StaticSemanticAnalysis --rule Parallelism-UnsafeVarAccess --ir your.ir -o races.txt --output_rfg your.rfg.Set a
RACESenvironment variable to point to yourraces.txt.Run
gravis --quit --script Bauhaus/example/scripts/create_callgraphs_pdf.py your.rfgCheck the generated PDF output.
8.1.16.4. Checking Options¶
There are two stylecheck rules that can be used to ensure correct usage of critical regions and access to global variables:
Parallelism-IncorrectCriticalRegion: Critical regions must obey certain layout rules.
This check ensures
that enter/exit markers only appear inside statement sequences;
that each enter marker has exactly one matching exit marker later on in the same statement sequence;
that each exit marker has exactly one matching enter marker earlier on in the same statement sequence.
By default, the check for correct enter/exit usage only checks the toplevel statement sequence of each function. This check can be configured to be performed on each statement sequence inside a function with the option
check_all_statement_sequences.Enter and Exit markers can be macros expanding to
asm()statements ifenter_critical_macro/exit_critical_macroare configured, or can be functions ifenter_critical_function/exit_critical_functionare configured. This configuration has to be applied to both,Parallelism-IncorrectCriticalRegionandParallelism-UnsafeVarAccessif both are active.The check
Parallelism-IncorrectCriticalRegionis very strict on the use of the enter/exit markers as they have to be used pairwise inside of a syntactical statement sequence. If you prefer a more relaxed verification, you can alternatively enable the optionreport_cfg_based_critical_region_issuesof the RuleParallelism-UnsafeVarAccess. This activates a control-flow based check, so that the number of enter and exit calls have to match on each control-flow path through a function.Example of control-flow based verification vs. syntactical¶void func() { EnterCriticalRegion(); switch (cond) { case 1: ExitCriticalRegion(); /* see default branch below */ break; default: ExitCriticalRegion(); /* Called from two locations on different branches: Violation only with Parallelism-IncorrectCriticalRegion */ break; }; ExitCriticalRegion(); /* Called a second time here: Violation with Parallelism-IncorrectCriticalRegion and with Parallelism-UnsafeVarAccess if report_cfg_based_critical_region_issues is enabled */ }
Parallelism-UnsafeVarAccess: Do not access global variables or static fields outside critical region.
This check reports accesses to the same global variable from within different tasks (or tasks and interrupts) that are not guarded inside a critical region using the enter/exit markers.
For the analysis to work correctly, you have to configure the individual partitions and their entry points with the option
partitionsin a Python layer of the configuration. Please refer to that option’s description for details.Additionally, you can configure a list of function names which shall be excluded from the access check with the option
excluded_names(e.g. useful to exclude init functions). If you want to exclude whole subgraphs of the call graph (and not just single functions), you can configure the root of these subgraphs in the optionexcluded_subgraphs.Configuration of the enter/exit markers is the same as for rule
Parallelism-IncorrectCriticalRegion.Configuration of the task entry points and (optional) function pointer arrays per task partition ():
Example configuration of partitions¶# Copyright (C) 2025 Axivion GmbH # Copyright (C) 2025 The Qt Company GmbH, a subsidiary of The Qt Group # https://www.qt.io # pyright: reportUndefinedVariable=false # configuration of partitions analysis['Parallelism-UnsafeVarAccess'].partitions = { 'Task1': {'entries': ['Task1_Entry'], 'vectors': ['task1_CbArray']}, 'Task2': { 'entries': ['Task2_Entry'], 'vectors': ['task2_CbArray', 'task2_CbArray2'], }, 'Task3': {'entries': ['Task3_EntryA', 'Task3_EntryB']}, 'IRQ': {'priority': 1, 'entries': ['IRQHandler']}, 'FIQ': {'priority': 2, 'entries': ['FIQHandler']}, }
The option
strict_prioritiesprovides a way to avoid issues for cases where high-priority tasks and interrupts are implemented without critical regions. Please refer to the option description for details.Furthermore, you can configure whether variables that have only read or only write accesses are output as well with the option
show_identical_access(default is False).If you want to see all variable accesses, even the safe ones (consistently contained in critical regions), this can be configured with the option
output_safe_accesses(default is False).