Our team is constantly enhancing the PVS-Studio static analyzer to boost the code analysis efficiency. In this article, we will explore the latest analyzer updates that significantly improve the analysis of Unreal Engine projects.
False positive caused by check macro
It all started when Epic Games, the Unreal Engine developers, reported a large number of false positives when using built-in macros:
struct AAA
{
virtual int aaa() { return 1; }
virtual void bbb() {}
};
static void ccc(AAA* ptr)
{
check(ptr);
int ddd = ptr->aaa();
if (ptr) // <= V595 The 'ptr' pointer was utilized before
// it was verified agains nullptr.
{
ptr->bbb();
}
}
It's expected that the ptr pointer will be non-null if the control flow moves to the next instruction after the pointer has been passed to the check macro. The analyzer had a different perspective here and issued false positives. Epic Games also stated that the issue occurred only when using the Shipping configuration.
The Shipping configuration is typically intended for a release build, but developers often use it during the development stage for the following purposes:
- to apply maximum optimizations: the Shipping configuration enables all available optimizations, so developers can detect potential performance issues early.
- to keep debugging capability: despite optimizations, the Shipping configuration still enables the use of debugging information and some debugging tools.
When we looked into the issue more closely, we discovered that the CA_ASSUME macro, which is located inside the check macro, was causing the false positive:
#define CA_ASSUME( Expr ) __analysis_assume( !!( Expr ) )
We've already mentioned that Shipping builds are similar to the Release ones. So, once preprocessed, the macro expands to the noop operation (i.e., it does nothing). It looks like this:
{
....
do
{
__noop(!!(ptr));
}
while ((0, 0) __pragma(warning(pop)));
};
Here's an interesting fact: Epic Games informed us that Clang Static Analyzer doesn't issue false positives for the pattern. This inspired us to look at the CA_ASSUME macro implementation for Clang Static Analyzer_:_
__declspec(dllimport, noreturn) void CA_AssumeNoReturn();
#define CA_ASSUME( Expr ) ( __builtin_expect(!bool(Expr), 0) \
? CA_AssumeNoReturn() \
: (void)0 )
The macro expands to a noreturn function call if the passed expression is false. This helps Clang Static Analyzer determine that the pointer after the macro call is non-null.
After discussing this with Epic Games, we decided to create our own implementation of the check macros to help the PVS-Studio analyzer work correctly. The feature will be available starting with the PVS-Studio 7.33 and Unreal Engine 5.5 releases.
New report format
We worked together with the Unreal Engine developers to change the intermediate format for warning output. Now the C and C++ analyzer core issues warnings in a new format. The support for multi-file navigation in the code is its main advantage for the user. If you are using the UnrealBuildTool integration to check your Unreal Engine projects, update the analyzer to version 7.30 (and above) to use this feature.
Now you can easily determine where exactly the null pointer was dereferenced and the path it took to get there.
SN-DBS analysis support
SN-DBS is a distributed build system designed to significantly enhance compilation performance. This is done by distributing build tasks across multiple nodes, which is especially useful for large-scale projects.
When PVS-Studio tried to analyze a project built in SN-DBS, an issue arose: the tool analyzed only the files that were processed on the master node. The build logs contained an error message.
The fix for this issue is ready and is expected in the upcoming Unreal Engine 5.5 release.
New settings in UnrealBuildTool
How to integrate PVS-Studio using target file
You can integrate PVS-Studio into your build process by modifying the target file. This is particularly useful if you need to frequently regenerate project files.
To integrate PVS-Studio, add the 'StaticAnalyzer' parameter with the 'PVSStudio' value:
For version 5.0 and earlier:
WindowsPlatform.StaticAnalyzer = WindowsStaticAnalyzer.PVSStudio;
For version 5.1 and later:
StaticAnalyzer = StaticAnalyzer.PVSStudio;
In addition to the 'StaticAnalyzer' parameter, you can add other settings, which we'll talk about below.
How to disable analysis of autogenerated files
Starting with Unreal Engine 5.4, the analysis of autogenerated files (that have the *.gen.cpp extension) is disabled by default. This was done to reduce the analysis time for large projects. You can revert to the old behavior using a special flag:
-StaticAnalyzerIncludeGenerated
Setting the verbosity of warning output in UnrealBuildTool
Unreal Engine 5.4 introduced a setting that sets the level of warnings issued by UnrealBuildTool during the analysis. It doesn't affect the PVS-Studio work but may cause a slowdown when getting a report. This is due to the setting that is enabled by default. As a result, UnrealBuildTool issues more warnings than necessary, increasing the time it takes to generate and process the report.
To disable it and avoid the issue described above, add the following flag to the UnrealBuildTool command line:
-StaticAnalyzerPVSPrintLevel = 0
Otherwise, you can configure this setting in the *.Target.cs file:
StaticAnalyzerPVSPrintLevel = 0;
How to disable Unreal Engine core analysis
Starting with Unreal Engine 5.4, a setting to run the analyzer only for project files (skipping the Unreal Engine core module) has become available. It enables you to boost the analysis speed, especially if no changes have been made to the Unreal Engine source code.
To enable the setting, add the following flag to the UnrealBuildTool command line:
-StaticAnalyzerProjectOnly
Otherwise, you can configure this setting in the *.Target.cs file:
bStaticAnalyzerProjectOnly = true;
Memory consumption optimization when analyzing C++ template instantiation
Users may experience increased memory consumption when analyzing Unreal Engine projects. There are two main reasons for this:
- The Single Compilation Unit technology: Unreal Engine uses the Single Compilation Unit technology, also known as unity build or jumbo build. This means that N translation units are merged into one. While this approach has advantages for both compilation and analysis, it also results in larger pre-processed files to analyze, longer processing times, and increased memory consumption.
- Extensive use of templates: Unreal Engine is a modern C++ project, so it makes extensive use of templates. Well, we have a modern analyzer that analyzes all instantiations, which increases both memory consumption and analysis time.
We've significantly optimized the analyzer performance (in terms of analysis time and memory consumption) and have made great progress there. However, we still recommend you to disable the analysis of unity packs when working with large projects. You can learn how to do this here.
New diagnostic rules for Unreal Engine projects
PVS-Studio provides diagnostic rules for detecting bugs specific to Unreal Engine projects:
- V1100: the analyzer has detected a non-static class data member that was declared as a pointer to a type derived from 'UObject' inside a class/structure that is not derived from the 'UObject' type. The Unreal Engine garbage collector may destroy an object addressed by this pointer.
- V1102: the analyzer has detected a declaration that does not comply with Naming Conventions for Unreal Engine projects. Compliance with the conventions is required for the correct operation of the Unreal Header Tool.
We've already received feedback from our users about false positives and have fixed some of them. We're currently fixing the remaining issues.
We also welcome any suggestions for new rules. You can learn more about it here.
Eliminating false positives in old diagnostic rules
Unreal Engine users have been experiencing a large number of false positives from the General Analysis diagnostic rules. In response, we've started addressing these issues. We've now fixed the diagnostic rules that were causing the most false positives in the Unreal Engine code:
- V542: a very suspicious explicit type conversion
- V557: a potential memory access outside an array
- V623: a possible error occurring when handling the ternary operator '?:'
- V758: a reference that may become invalid has been detected
- V781: the value of a variable is used as the size or index of an array before the check
We're not done yet! The team is currently enhancing the next set of diagnostic rules.
Conclusion
We hope that the enhancements described in this article will help both Epic Games developers and Unreal Engine users.
If you have ideas for analyzer enhancements or new diagnostic rules for Unreal Engine projects, please feel free to share them with us. We're always open to new suggestions.
Stay tuned for all the latest updates :)
Top comments (0)