diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index a674af7be0ef..785e064e72a9 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -1,651 +1,659 @@ //===--- Checkers.td - Static Analyzer Checkers -===-----------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// include "clang/StaticAnalyzer/Checkers/CheckerBase.td" //===----------------------------------------------------------------------===// // Packages. //===----------------------------------------------------------------------===// // The Alpha package is for checkers that have too many false positives to be // turned on by default. The hierarchy under Alpha should be organized in the // hierarchy checkers would have had if they were truly at the top level. // (For example, a Cocoa-specific checker that is alpha should be in // alpha.osx.cocoa). def Alpha : Package<"alpha">; def Core : Package<"core">; def CoreBuiltin : Package<"builtin">, InPackage; def CoreUninitialized : Package<"uninitialized">, InPackage; def CoreAlpha : Package<"core">, InPackage, Hidden; // The OptIn package is for checkers that are not alpha and that would normally // be on by default but where the driver does not have enough information to // determine when they are applicable. For example, localizability checkers fit // this criterion because the driver cannot determine whether a project is // localized or not -- this is best determined at the IDE or build-system level. // // The checker hierarchy under OptIn should mirror that in Alpha: checkers // should be organized as if they were at the top level. // // Note: OptIn is *not* intended for checkers that are too noisy to be on by // default. Such checkers belong in the alpha package. def OptIn : Package<"optin">; def Nullability : Package<"nullability">; def Cplusplus : Package<"cplusplus">; def CplusplusAlpha : Package<"cplusplus">, InPackage, Hidden; def DeadCode : Package<"deadcode">; def DeadCodeAlpha : Package<"deadcode">, InPackage, Hidden; def Performance : Package<"performance">, InPackage; def Security : Package <"security">; def InsecureAPI : Package<"insecureAPI">, InPackage; def SecurityAlpha : Package<"security">, InPackage, Hidden; def Taint : Package<"taint">, InPackage, Hidden; def Unix : Package<"unix">; def UnixAlpha : Package<"unix">, InPackage, Hidden; def CString : Package<"cstring">, InPackage, Hidden; def CStringAlpha : Package<"cstring">, InPackage, Hidden; def OSX : Package<"osx">; def OSXAlpha : Package<"osx">, InPackage, Hidden; def OSXOptIn : Package<"osx">, InPackage; def Cocoa : Package<"cocoa">, InPackage; def CocoaAlpha : Package<"cocoa">, InPackage, Hidden; def CocoaOptIn : Package<"cocoa">, InPackage; def CoreFoundation : Package<"coreFoundation">, InPackage; def Containers : Package<"containers">, InPackage; def LocalizabilityAlpha : Package<"localizability">, InPackage; def LocalizabilityOptIn : Package<"localizability">, InPackage; +def MPI : Package<"mpi">, InPackage; + def LLVM : Package<"llvm">; def Debug : Package<"debug">; //===----------------------------------------------------------------------===// // Core Checkers. //===----------------------------------------------------------------------===// let ParentPackage = Core in { def DereferenceChecker : Checker<"NullDereference">, HelpText<"Check for dereferences of null pointers">, DescFile<"DereferenceChecker.cpp">; def CallAndMessageChecker : Checker<"CallAndMessage">, HelpText<"Check for logical errors for function calls and Objective-C message expressions (e.g., uninitialized arguments, null function pointers)">, DescFile<"CallAndMessageChecker.cpp">; def NonNullParamChecker : Checker<"NonNullParamChecker">, HelpText<"Check for null pointers passed as arguments to a function whose arguments are references or marked with the 'nonnull' attribute">, DescFile<"NonNullParamChecker.cpp">; def VLASizeChecker : Checker<"VLASize">, HelpText<"Check for declarations of VLA of undefined or zero size">, DescFile<"VLASizeChecker.cpp">; def DivZeroChecker : Checker<"DivideZero">, HelpText<"Check for division by zero">, DescFile<"DivZeroChecker.cpp">; def UndefResultChecker : Checker<"UndefinedBinaryOperatorResult">, HelpText<"Check for undefined results of binary operators">, DescFile<"UndefResultChecker.cpp">; def StackAddrEscapeChecker : Checker<"StackAddressEscape">, HelpText<"Check that addresses to stack memory do not escape the function">, DescFile<"StackAddrEscapeChecker.cpp">; def DynamicTypePropagation : Checker<"DynamicTypePropagation">, HelpText<"Generate dynamic type information">, DescFile<"DynamicTypePropagation.cpp">; } // end "core" let ParentPackage = CoreAlpha in { def BoolAssignmentChecker : Checker<"BoolAssignment">, HelpText<"Warn about assigning non-{0,1} values to Boolean variables">, DescFile<"BoolAssignmentChecker.cpp">; def CastSizeChecker : Checker<"CastSize">, HelpText<"Check when casting a malloc'ed type T, whether the size is a multiple of the size of T">, DescFile<"CastSizeChecker.cpp">; def CastToStructChecker : Checker<"CastToStruct">, HelpText<"Check for cast from non-struct pointer to struct pointer">, DescFile<"CastToStructChecker.cpp">; def IdenticalExprChecker : Checker<"IdenticalExpr">, HelpText<"Warn about unintended use of identical expressions in operators">, DescFile<"IdenticalExprChecker.cpp">; def FixedAddressChecker : Checker<"FixedAddr">, HelpText<"Check for assignment of a fixed address to a pointer">, DescFile<"FixedAddressChecker.cpp">; def PointerArithChecker : Checker<"PointerArithm">, HelpText<"Check for pointer arithmetic on locations other than array elements">, DescFile<"PointerArithChecker">; def PointerSubChecker : Checker<"PointerSub">, HelpText<"Check for pointer subtractions on two pointers pointing to different memory chunks">, DescFile<"PointerSubChecker">; def SizeofPointerChecker : Checker<"SizeofPtr">, HelpText<"Warn about unintended use of sizeof() on pointer expressions">, DescFile<"CheckSizeofPointer.cpp">; def CallAndMessageUnInitRefArg : Checker<"CallAndMessageUnInitRefArg">, HelpText<"Check for logical errors for function calls and Objective-C message expressions (e.g., uninitialized arguments, null function pointers, and pointer to undefined variables)">, DescFile<"CallAndMessageChecker.cpp">; def TestAfterDivZeroChecker : Checker<"TestAfterDivZero">, HelpText<"Check for division by variable that is later compared against 0. Either the comparison is useless or there is division by zero.">, DescFile<"TestAfterDivZeroChecker.cpp">; def DynamicTypeChecker : Checker<"DynamicTypeChecker">, HelpText<"Check for cases where the dynamic and the static type of an object are unrelated.">, DescFile<"DynamicTypeChecker.cpp">; } // end "alpha.core" let ParentPackage = Nullability in { def NullPassedToNonnullChecker : Checker<"NullPassedToNonnull">, HelpText<"Warns when a null pointer is passed to a pointer which has a _Nonnull type.">, DescFile<"NullabilityChecker.cpp">; def NullReturnedFromNonnullChecker : Checker<"NullReturnedFromNonnull">, HelpText<"Warns when a null pointer is returned from a function that has _Nonnull return type.">, DescFile<"NullabilityChecker.cpp">; def NullableDereferencedChecker : Checker<"NullableDereferenced">, HelpText<"Warns when a nullable pointer is dereferenced.">, DescFile<"NullabilityChecker.cpp">; def NullablePassedToNonnullChecker : Checker<"NullablePassedToNonnull">, HelpText<"Warns when a nullable pointer is passed to a pointer which has a _Nonnull type.">, DescFile<"NullabilityChecker.cpp">; def NullableReturnedFromNonnullChecker : Checker<"NullablePassedToNonnull">, HelpText<"Warns when a nullable pointer is returned from a function that has _Nonnull return type.">, DescFile<"NullabilityChecker.cpp">; } // end "nullability" //===----------------------------------------------------------------------===// // Evaluate "builtin" functions. //===----------------------------------------------------------------------===// let ParentPackage = CoreBuiltin in { def NoReturnFunctionChecker : Checker<"NoReturnFunctions">, HelpText<"Evaluate \"panic\" functions that are known to not return to the caller">, DescFile<"NoReturnFunctionChecker.cpp">; def BuiltinFunctionChecker : Checker<"BuiltinFunctions">, HelpText<"Evaluate compiler builtin functions (e.g., alloca())">, DescFile<"BuiltinFunctionChecker.cpp">; } // end "core.builtin" //===----------------------------------------------------------------------===// // Uninitialized values checkers. //===----------------------------------------------------------------------===// let ParentPackage = CoreUninitialized in { def UndefinedArraySubscriptChecker : Checker<"ArraySubscript">, HelpText<"Check for uninitialized values used as array subscripts">, DescFile<"UndefinedArraySubscriptChecker.cpp">; def UndefinedAssignmentChecker : Checker<"Assign">, HelpText<"Check for assigning uninitialized values">, DescFile<"UndefinedAssignmentChecker.cpp">; def UndefBranchChecker : Checker<"Branch">, HelpText<"Check for uninitialized values used as branch conditions">, DescFile<"UndefBranchChecker.cpp">; def UndefCapturedBlockVarChecker : Checker<"CapturedBlockVariable">, HelpText<"Check for blocks that capture uninitialized values">, DescFile<"UndefCapturedBlockVarChecker.cpp">; def ReturnUndefChecker : Checker<"UndefReturn">, HelpText<"Check for uninitialized values being returned to the caller">, DescFile<"ReturnUndefChecker.cpp">; } // end "core.uninitialized" //===----------------------------------------------------------------------===// // C++ checkers. //===----------------------------------------------------------------------===// let ParentPackage = Cplusplus in { def NewDeleteChecker : Checker<"NewDelete">, HelpText<"Check for double-free and use-after-free problems. Traces memory managed by new/delete.">, DescFile<"MallocChecker.cpp">; def NewDeleteLeaksChecker : Checker<"NewDeleteLeaks">, HelpText<"Check for memory leaks. Traces memory managed by new/delete.">, DescFile<"MallocChecker.cpp">; } // end: "cplusplus" let ParentPackage = CplusplusAlpha in { def VirtualCallChecker : Checker<"VirtualCall">, HelpText<"Check virtual function calls during construction or destruction">, DescFile<"VirtualCallChecker.cpp">; } // end: "alpha.cplusplus" //===----------------------------------------------------------------------===// // Deadcode checkers. //===----------------------------------------------------------------------===// let ParentPackage = DeadCode in { def DeadStoresChecker : Checker<"DeadStores">, HelpText<"Check for values stored to variables that are never read afterwards">, DescFile<"DeadStoresChecker.cpp">; } // end DeadCode let ParentPackage = DeadCodeAlpha in { def UnreachableCodeChecker : Checker<"UnreachableCode">, HelpText<"Check unreachable code">, DescFile<"UnreachableCodeChecker.cpp">; } // end "alpha.deadcode" //===----------------------------------------------------------------------===// // Performance checkers. //===----------------------------------------------------------------------===// let ParentPackage = Performance in { def PaddingChecker : Checker<"Padding">, HelpText<"Check for excessively padded structs.">, DescFile<"PaddingChecker.cpp">; } // end: "padding" //===----------------------------------------------------------------------===// // Security checkers. //===----------------------------------------------------------------------===// let ParentPackage = InsecureAPI in { def gets : Checker<"gets">, HelpText<"Warn on uses of the 'gets' function">, DescFile<"CheckSecuritySyntaxOnly.cpp">; def getpw : Checker<"getpw">, HelpText<"Warn on uses of the 'getpw' function">, DescFile<"CheckSecuritySyntaxOnly.cpp">; def mktemp : Checker<"mktemp">, HelpText<"Warn on uses of the 'mktemp' function">, DescFile<"CheckSecuritySyntaxOnly.cpp">; def mkstemp : Checker<"mkstemp">, HelpText<"Warn when 'mkstemp' is passed fewer than 6 X's in the format string">, DescFile<"CheckSecuritySyntaxOnly.cpp">; def rand : Checker<"rand">, HelpText<"Warn on uses of the 'rand', 'random', and related functions">, DescFile<"CheckSecuritySyntaxOnly.cpp">; def strcpy : Checker<"strcpy">, HelpText<"Warn on uses of the 'strcpy' and 'strcat' functions">, DescFile<"CheckSecuritySyntaxOnly.cpp">; def vfork : Checker<"vfork">, HelpText<"Warn on uses of the 'vfork' function">, DescFile<"CheckSecuritySyntaxOnly.cpp">; def UncheckedReturn : Checker<"UncheckedReturn">, HelpText<"Warn on uses of functions whose return values must be always checked">, DescFile<"CheckSecuritySyntaxOnly.cpp">; } let ParentPackage = Security in { def FloatLoopCounter : Checker<"FloatLoopCounter">, HelpText<"Warn on using a floating point value as a loop counter (CERT: FLP30-C, FLP30-CPP)">, DescFile<"CheckSecuritySyntaxOnly.cpp">; } let ParentPackage = SecurityAlpha in { def ArrayBoundChecker : Checker<"ArrayBound">, HelpText<"Warn about buffer overflows (older checker)">, DescFile<"ArrayBoundChecker.cpp">; def ArrayBoundCheckerV2 : Checker<"ArrayBoundV2">, HelpText<"Warn about buffer overflows (newer checker)">, DescFile<"ArrayBoundCheckerV2.cpp">; def ReturnPointerRangeChecker : Checker<"ReturnPtrRange">, HelpText<"Check for an out-of-bound pointer being returned to callers">, DescFile<"ReturnPointerRangeChecker.cpp">; def MallocOverflowSecurityChecker : Checker<"MallocOverflow">, HelpText<"Check for overflows in the arguments to malloc()">, DescFile<"MallocOverflowSecurityChecker.cpp">; } // end "alpha.security" //===----------------------------------------------------------------------===// // Taint checkers. //===----------------------------------------------------------------------===// let ParentPackage = Taint in { def GenericTaintChecker : Checker<"TaintPropagation">, HelpText<"Generate taint information used by other checkers">, DescFile<"GenericTaintChecker.cpp">; } // end "alpha.security.taint" //===----------------------------------------------------------------------===// // Unix API checkers. //===----------------------------------------------------------------------===// let ParentPackage = Unix in { def UnixAPIChecker : Checker<"API">, HelpText<"Check calls to various UNIX/Posix functions">, DescFile<"UnixAPIChecker.cpp">; def MallocChecker: Checker<"Malloc">, HelpText<"Check for memory leaks, double free, and use-after-free problems. Traces memory managed by malloc()/free().">, DescFile<"MallocChecker.cpp">; def MallocSizeofChecker : Checker<"MallocSizeof">, HelpText<"Check for dubious malloc arguments involving sizeof">, DescFile<"MallocSizeofChecker.cpp">; def MismatchedDeallocatorChecker : Checker<"MismatchedDeallocator">, HelpText<"Check for mismatched deallocators.">, DescFile<"MallocChecker.cpp">; def VforkChecker : Checker<"Vfork">, HelpText<"Check for proper usage of vfork">, DescFile<"VforkChecker.cpp">; } // end "unix" let ParentPackage = UnixAlpha in { def ChrootChecker : Checker<"Chroot">, HelpText<"Check improper use of chroot">, DescFile<"ChrootChecker.cpp">; def PthreadLockChecker : Checker<"PthreadLock">, HelpText<"Simple lock -> unlock checker">, DescFile<"PthreadLockChecker.cpp">; def StreamChecker : Checker<"Stream">, HelpText<"Check stream handling functions">, DescFile<"StreamChecker.cpp">; def SimpleStreamChecker : Checker<"SimpleStream">, HelpText<"Check for misuses of stream APIs">, DescFile<"SimpleStreamChecker.cpp">; } // end "alpha.unix" let ParentPackage = CString in { def CStringNullArg : Checker<"NullArg">, HelpText<"Check for null pointers being passed as arguments to C string functions">, DescFile<"CStringChecker.cpp">; def CStringSyntaxChecker : Checker<"BadSizeArg">, HelpText<"Check the size argument passed into C string functions for common erroneous patterns">, DescFile<"CStringSyntaxChecker.cpp">; } let ParentPackage = CStringAlpha in { def CStringOutOfBounds : Checker<"OutOfBounds">, HelpText<"Check for out-of-bounds access in string functions">, DescFile<"CStringChecker.cpp">; def CStringBufferOverlap : Checker<"BufferOverlap">, HelpText<"Checks for overlap in two buffer arguments">, DescFile<"CStringChecker.cpp">; def CStringNotNullTerm : Checker<"NotNullTerminated">, HelpText<"Check for arguments which are not null-terminating strings">, DescFile<"CStringChecker.cpp">; } //===----------------------------------------------------------------------===// // Mac OS X, Cocoa, and Core Foundation checkers. //===----------------------------------------------------------------------===// let ParentPackage = OSX in { def MacOSXAPIChecker : Checker<"API">, InPackage, HelpText<"Check for proper uses of various Apple APIs">, DescFile<"MacOSXAPIChecker.cpp">; def MacOSKeychainAPIChecker : Checker<"SecKeychainAPI">, InPackage, HelpText<"Check for proper uses of Secure Keychain APIs">, DescFile<"MacOSKeychainAPIChecker.cpp">; } // end "osx" let ParentPackage = Cocoa in { def ObjCAtSyncChecker : Checker<"AtSync">, HelpText<"Check for nil pointers used as mutexes for @synchronized">, DescFile<"ObjCAtSyncChecker.cpp">; def NilArgChecker : Checker<"NilArg">, HelpText<"Check for prohibited nil arguments to ObjC method calls">, DescFile<"BasicObjCFoundationChecks.cpp">; def ClassReleaseChecker : Checker<"ClassRelease">, HelpText<"Check for sending 'retain', 'release', or 'autorelease' directly to a Class">, DescFile<"BasicObjCFoundationChecks.cpp">; def VariadicMethodTypeChecker : Checker<"VariadicMethodTypes">, HelpText<"Check for passing non-Objective-C types to variadic collection " "initialization methods that expect only Objective-C types">, DescFile<"BasicObjCFoundationChecks.cpp">; def NSAutoreleasePoolChecker : Checker<"NSAutoreleasePool">, HelpText<"Warn for suboptimal uses of NSAutoreleasePool in Objective-C GC mode">, DescFile<"NSAutoreleasePoolChecker.cpp">; def ObjCMethSigsChecker : Checker<"IncompatibleMethodTypes">, HelpText<"Warn about Objective-C method signatures with type incompatibilities">, DescFile<"CheckObjCInstMethSignature.cpp">; def ObjCUnusedIvarsChecker : Checker<"UnusedIvars">, HelpText<"Warn about private ivars that are never used">, DescFile<"ObjCUnusedIVarsChecker.cpp">; def ObjCSelfInitChecker : Checker<"SelfInit">, HelpText<"Check that 'self' is properly initialized inside an initializer method">, DescFile<"ObjCSelfInitChecker.cpp">; def ObjCLoopChecker : Checker<"Loops">, HelpText<"Improved modeling of loops using Cocoa collection types">, DescFile<"BasicObjCFoundationChecks.cpp">; def ObjCNonNilReturnValueChecker : Checker<"NonNilReturnValue">, HelpText<"Model the APIs that are guaranteed to return a non-nil value">, DescFile<"BasicObjCFoundationChecks.cpp">; def ObjCSuperCallChecker : Checker<"MissingSuperCall">, HelpText<"Warn about Objective-C methods that lack a necessary call to super">, DescFile<"ObjCMissingSuperCallChecker.cpp">; def NSErrorChecker : Checker<"NSError">, HelpText<"Check usage of NSError** parameters">, DescFile<"NSErrorChecker.cpp">; def RetainCountChecker : Checker<"RetainCount">, HelpText<"Check for leaks and improper reference count management">, DescFile<"RetainCountChecker.cpp">; def ObjCGenericsChecker : Checker<"ObjCGenerics">, HelpText<"Check for type errors when using Objective-C generics">, DescFile<"DynamicTypePropagation.cpp">; def ObjCDeallocChecker : Checker<"Dealloc">, HelpText<"Warn about Objective-C classes that lack a correct implementation of -dealloc">, DescFile<"CheckObjCDealloc.cpp">; def ObjCSuperDeallocChecker : Checker<"SuperDealloc">, HelpText<"Warn about improper use of '[super dealloc]' in Objective-C">, DescFile<"ObjCSuperDeallocChecker.cpp">; } // end "osx.cocoa" let ParentPackage = CocoaAlpha in { def InstanceVariableInvalidation : Checker<"InstanceVariableInvalidation">, HelpText<"Check that the invalidatable instance variables are invalidated in the methods annotated with objc_instance_variable_invalidator">, DescFile<"IvarInvalidationChecker.cpp">; def MissingInvalidationMethod : Checker<"MissingInvalidationMethod">, HelpText<"Check that the invalidation methods are present in classes that contain invalidatable instance variables">, DescFile<"IvarInvalidationChecker.cpp">; def DirectIvarAssignment : Checker<"DirectIvarAssignment">, HelpText<"Check for direct assignments to instance variables">, DescFile<"DirectIvarAssignment.cpp">; def DirectIvarAssignmentForAnnotatedFunctions : Checker<"DirectIvarAssignmentForAnnotatedFunctions">, HelpText<"Check for direct assignments to instance variables in the methods annotated with objc_no_direct_instance_variable_assignment">, DescFile<"DirectIvarAssignment.cpp">; } // end "alpha.osx.cocoa" let ParentPackage = CoreFoundation in { def CFNumberCreateChecker : Checker<"CFNumber">, HelpText<"Check for proper uses of CFNumberCreate">, DescFile<"BasicObjCFoundationChecks.cpp">; def CFRetainReleaseChecker : Checker<"CFRetainRelease">, HelpText<"Check for null arguments to CFRetain/CFRelease/CFMakeCollectable">, DescFile<"BasicObjCFoundationChecks.cpp">; def CFErrorChecker : Checker<"CFError">, HelpText<"Check usage of CFErrorRef* parameters">, DescFile<"NSErrorChecker.cpp">; } let ParentPackage = Containers in { def ObjCContainersASTChecker : Checker<"PointerSizedValues">, HelpText<"Warns if 'CFArray', 'CFDictionary', 'CFSet' are created with non-pointer-size values">, DescFile<"ObjCContainersASTChecker.cpp">; def ObjCContainersChecker : Checker<"OutOfBounds">, HelpText<"Checks for index out-of-bounds when using 'CFArray' API">, DescFile<"ObjCContainersChecker.cpp">; } let ParentPackage = LocalizabilityOptIn in { def NonLocalizedStringChecker : Checker<"NonLocalizedStringChecker">, HelpText<"Warns about uses of non-localized NSStrings passed to UI methods expecting localized NSStrings">, DescFile<"LocalizationChecker.cpp">; def EmptyLocalizationContextChecker : Checker<"EmptyLocalizationContextChecker">, HelpText<"Check that NSLocalizedString macros include a comment for context">, DescFile<"LocalizationChecker.cpp">; } let ParentPackage = LocalizabilityAlpha in { def PluralMisuseChecker : Checker<"PluralMisuseChecker">, HelpText<"Warns against using one vs. many plural pattern in code when generating localized strings.">, DescFile<"LocalizationChecker.cpp">; } +let ParentPackage = MPI in { + def MPIChecker : Checker<"MPI-Checker">, + HelpText<"Checks MPI code">, + DescFile<"MPIChecker.cpp">; +} + //===----------------------------------------------------------------------===// // Checkers for LLVM development. //===----------------------------------------------------------------------===// def LLVMConventionsChecker : Checker<"Conventions">, InPackage, HelpText<"Check code for LLVM codebase conventions">, DescFile<"LLVMConventionsChecker.cpp">; //===----------------------------------------------------------------------===// // Debugging checkers (for analyzer development). //===----------------------------------------------------------------------===// let ParentPackage = Debug in { def DominatorsTreeDumper : Checker<"DumpDominators">, HelpText<"Print the dominance tree for a given CFG">, DescFile<"DebugCheckers.cpp">; def LiveVariablesDumper : Checker<"DumpLiveVars">, HelpText<"Print results of live variable analysis">, DescFile<"DebugCheckers.cpp">; def CFGViewer : Checker<"ViewCFG">, HelpText<"View Control-Flow Graphs using GraphViz">, DescFile<"DebugCheckers.cpp">; def CFGDumper : Checker<"DumpCFG">, HelpText<"Display Control-Flow Graphs">, DescFile<"DebugCheckers.cpp">; def CallGraphViewer : Checker<"ViewCallGraph">, HelpText<"View Call Graph using GraphViz">, DescFile<"DebugCheckers.cpp">; def CallGraphDumper : Checker<"DumpCallGraph">, HelpText<"Display Call Graph">, DescFile<"DebugCheckers.cpp">; def ConfigDumper : Checker<"ConfigDumper">, HelpText<"Dump config table">, DescFile<"DebugCheckers.cpp">; def TraversalDumper : Checker<"DumpTraversal">, HelpText<"Print branch conditions as they are traversed by the engine">, DescFile<"TraversalChecker.cpp">; def CallDumper : Checker<"DumpCalls">, HelpText<"Print calls as they are traversed by the engine">, DescFile<"TraversalChecker.cpp">; def AnalyzerStatsChecker : Checker<"Stats">, HelpText<"Emit warnings with analyzer statistics">, DescFile<"AnalyzerStatsChecker.cpp">; def TaintTesterChecker : Checker<"TaintTest">, HelpText<"Mark tainted symbols as such.">, DescFile<"TaintTesterChecker.cpp">; def ExprInspectionChecker : Checker<"ExprInspection">, HelpText<"Check the analyzer's understanding of expressions">, DescFile<"ExprInspectionChecker.cpp">; def ExplodedGraphViewer : Checker<"ViewExplodedGraph">, HelpText<"View Exploded Graphs using GraphViz">, DescFile<"DebugCheckers.cpp">; def BugHashDumper : Checker<"DumpBugHash">, HelpText<"Dump the bug hash for all statements.">, DescFile<"DebugCheckers.cpp">; } // end "debug" diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h index 1d33301cf2fc..40cd4e4b3c4e 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -1,1343 +1,1365 @@ //== MemRegion.h - Abstract memory regions for static analysis --*- C++ -*--==// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines MemRegion and its subclasses. MemRegion defines a // partially-typed abstraction of memory useful for path-sensitive dataflow // analyses. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_MEMREGION_H #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_MEMREGION_H #include "clang/AST/ASTContext.h" #include "clang/AST/CharUnits.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/ErrorHandling.h" #include namespace clang { class LocationContext; class StackFrameContext; namespace ento { class CodeTextRegion; class MemRegionManager; class MemSpaceRegion; class SValBuilder; class SymbolicRegion; class VarRegion; /// Represent a region's offset within the top level base region. class RegionOffset { /// The base region. const MemRegion *R; /// The bit offset within the base region. Can be negative. int64_t Offset; public: // We're using a const instead of an enumeration due to the size required; // Visual Studio will only create enumerations of size int, not long long. static const int64_t Symbolic = INT64_MAX; RegionOffset() : R(nullptr) {} RegionOffset(const MemRegion *r, int64_t off) : R(r), Offset(off) {} const MemRegion *getRegion() const { return R; } bool hasSymbolicOffset() const { return Offset == Symbolic; } int64_t getOffset() const { assert(!hasSymbolicOffset()); return Offset; } bool isValid() const { return R; } }; //===----------------------------------------------------------------------===// // Base region classes. //===----------------------------------------------------------------------===// /// MemRegion - The root abstract class for all memory regions. class MemRegion : public llvm::FoldingSetNode { public: enum Kind { #define REGION(Id, Parent) Id ## Kind, #define REGION_RANGE(Id, First, Last) BEGIN_##Id = First, END_##Id = Last, #include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def" }; private: const Kind kind; protected: MemRegion(Kind k) : kind(k) {} virtual ~MemRegion(); public: ASTContext &getContext() const; virtual void Profile(llvm::FoldingSetNodeID& ID) const = 0; virtual MemRegionManager* getMemRegionManager() const = 0; const MemSpaceRegion *getMemorySpace() const; const MemRegion *getBaseRegion() const; /// Check if the region is a subregion of the given region. virtual bool isSubRegionOf(const MemRegion *R) const; const MemRegion *StripCasts(bool StripBaseCasts = true) const; /// \brief If this is a symbolic region, returns the region. Otherwise, /// goes up the base chain looking for the first symbolic base region. const SymbolicRegion *getSymbolicBase() const; bool hasGlobalsOrParametersStorage() const; bool hasStackStorage() const; bool hasStackNonParametersStorage() const; bool hasStackParametersStorage() const; /// Compute the offset within the top level memory object. RegionOffset getAsOffset() const; /// \brief Get a string representation of a region for debug use. std::string getString() const; virtual void dumpToStream(raw_ostream &os) const; void dump() const; /// \brief Returns true if this region can be printed in a user-friendly way. virtual bool canPrintPretty() const; /// \brief Print the region for use in diagnostics. virtual void printPretty(raw_ostream &os) const; /// \brief Returns true if this region's textual representation can be used /// as part of a larger expression. virtual bool canPrintPrettyAsExpr() const; /// \brief Print the region as expression. /// /// When this region represents a subexpression, the method is for printing /// an expression containing it. virtual void printPrettyAsExpr(raw_ostream &os) const; Kind getKind() const { return kind; } template const RegionTy* getAs() const; virtual bool isBoundable() const { return false; } + + + /// Get descriptive name for memory region. The name is obtained from + /// the variable/field declaration retrieved from the memory region. + /// Regions that point to an element of an array are returned as: "arr[0]". + /// Regions that point to a struct are returned as: "st.var". + // + /// \param UseQuotes Set if the name should be quoted. + /// + /// \returns variable name for memory region + std::string getDescriptiveName(bool UseQuotes = true) const; + + + /// Retrieve source range from memory region. The range retrieval + /// is based on the decl obtained from the memory region. + /// For a VarRegion the range of the base region is returned. + /// For a FieldRegion the range of the field is returned. + /// If no declaration is found, an empty source range is returned. + /// The client is responsible for checking if the returned range is valid. + /// + /// \returns source range for declaration retrieved from memory region + clang::SourceRange sourceRange() const; }; /// MemSpaceRegion - A memory region that represents a "memory space"; /// for example, the set of global variables, the stack frame, etc. class MemSpaceRegion : public MemRegion { protected: MemRegionManager *Mgr; MemSpaceRegion(MemRegionManager *mgr, Kind k) : MemRegion(k), Mgr(mgr) { assert(classof(this)); } MemRegionManager* getMemRegionManager() const override { return Mgr; } public: bool isBoundable() const override { return false; } void Profile(llvm::FoldingSetNodeID &ID) const override; static bool classof(const MemRegion *R) { Kind k = R->getKind(); return k >= BEGIN_MEMSPACES && k <= END_MEMSPACES; } }; /// CodeSpaceRegion - The memory space that holds the executable code of /// functions and blocks. class CodeSpaceRegion : public MemSpaceRegion { friend class MemRegionManager; CodeSpaceRegion(MemRegionManager *mgr) : MemSpaceRegion(mgr, CodeSpaceRegionKind) {} public: void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion *R) { return R->getKind() == CodeSpaceRegionKind; } }; class GlobalsSpaceRegion : public MemSpaceRegion { virtual void anchor(); protected: GlobalsSpaceRegion(MemRegionManager *mgr, Kind k) : MemSpaceRegion(mgr, k) {} public: static bool classof(const MemRegion *R) { Kind k = R->getKind(); return k >= BEGIN_GLOBAL_MEMSPACES && k <= END_GLOBAL_MEMSPACES; } }; /// \brief The region of the static variables within the current CodeTextRegion /// scope. /// /// Currently, only the static locals are placed there, so we know that these /// variables do not get invalidated by calls to other functions. class StaticGlobalSpaceRegion : public GlobalsSpaceRegion { friend class MemRegionManager; const CodeTextRegion *CR; StaticGlobalSpaceRegion(MemRegionManager *mgr, const CodeTextRegion *cr) : GlobalsSpaceRegion(mgr, StaticGlobalSpaceRegionKind), CR(cr) {} public: void Profile(llvm::FoldingSetNodeID &ID) const override; void dumpToStream(raw_ostream &os) const override; const CodeTextRegion *getCodeRegion() const { return CR; } static bool classof(const MemRegion *R) { return R->getKind() == StaticGlobalSpaceRegionKind; } }; /// \brief The region for all the non-static global variables. /// /// This class is further split into subclasses for efficient implementation of /// invalidating a set of related global values as is done in /// RegionStoreManager::invalidateRegions (instead of finding all the dependent /// globals, we invalidate the whole parent region). class NonStaticGlobalSpaceRegion : public GlobalsSpaceRegion { protected: NonStaticGlobalSpaceRegion(MemRegionManager *mgr, Kind k) : GlobalsSpaceRegion(mgr, k) {} public: static bool classof(const MemRegion *R) { Kind k = R->getKind(); return k >= BEGIN_NON_STATIC_GLOBAL_MEMSPACES && k <= END_NON_STATIC_GLOBAL_MEMSPACES; } }; /// \brief The region containing globals which are defined in system/external /// headers and are considered modifiable by system calls (ex: errno). class GlobalSystemSpaceRegion : public NonStaticGlobalSpaceRegion { friend class MemRegionManager; GlobalSystemSpaceRegion(MemRegionManager *mgr) : NonStaticGlobalSpaceRegion(mgr, GlobalSystemSpaceRegionKind) {} public: void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion *R) { return R->getKind() == GlobalSystemSpaceRegionKind; } }; /// \brief The region containing globals which are considered not to be modified /// or point to data which could be modified as a result of a function call /// (system or internal). Ex: Const global scalars would be modeled as part of /// this region. This region also includes most system globals since they have /// low chance of being modified. class GlobalImmutableSpaceRegion : public NonStaticGlobalSpaceRegion { friend class MemRegionManager; GlobalImmutableSpaceRegion(MemRegionManager *mgr) : NonStaticGlobalSpaceRegion(mgr, GlobalImmutableSpaceRegionKind) {} public: void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion *R) { return R->getKind() == GlobalImmutableSpaceRegionKind; } }; /// \brief The region containing globals which can be modified by calls to /// "internally" defined functions - (for now just) functions other then system /// calls. class GlobalInternalSpaceRegion : public NonStaticGlobalSpaceRegion { friend class MemRegionManager; GlobalInternalSpaceRegion(MemRegionManager *mgr) : NonStaticGlobalSpaceRegion(mgr, GlobalInternalSpaceRegionKind) {} public: void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion *R) { return R->getKind() == GlobalInternalSpaceRegionKind; } }; class HeapSpaceRegion : public MemSpaceRegion { virtual void anchor(); friend class MemRegionManager; HeapSpaceRegion(MemRegionManager *mgr) : MemSpaceRegion(mgr, HeapSpaceRegionKind) {} public: void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion *R) { return R->getKind() == HeapSpaceRegionKind; } }; class UnknownSpaceRegion : public MemSpaceRegion { virtual void anchor(); friend class MemRegionManager; UnknownSpaceRegion(MemRegionManager *mgr) : MemSpaceRegion(mgr, UnknownSpaceRegionKind) {} public: void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion *R) { return R->getKind() == UnknownSpaceRegionKind; } }; class StackSpaceRegion : public MemSpaceRegion { private: const StackFrameContext *SFC; protected: StackSpaceRegion(MemRegionManager *mgr, Kind k, const StackFrameContext *sfc) : MemSpaceRegion(mgr, k), SFC(sfc) { assert(classof(this)); } public: const StackFrameContext *getStackFrame() const { return SFC; } void Profile(llvm::FoldingSetNodeID &ID) const override; static bool classof(const MemRegion *R) { Kind k = R->getKind(); return k >= BEGIN_STACK_MEMSPACES && k <= END_STACK_MEMSPACES; } }; class StackLocalsSpaceRegion : public StackSpaceRegion { virtual void anchor(); friend class MemRegionManager; StackLocalsSpaceRegion(MemRegionManager *mgr, const StackFrameContext *sfc) : StackSpaceRegion(mgr, StackLocalsSpaceRegionKind, sfc) {} public: void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion *R) { return R->getKind() == StackLocalsSpaceRegionKind; } }; class StackArgumentsSpaceRegion : public StackSpaceRegion { private: virtual void anchor(); friend class MemRegionManager; StackArgumentsSpaceRegion(MemRegionManager *mgr, const StackFrameContext *sfc) : StackSpaceRegion(mgr, StackArgumentsSpaceRegionKind, sfc) {} public: void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion *R) { return R->getKind() == StackArgumentsSpaceRegionKind; } }; /// SubRegion - A region that subsets another larger region. Most regions /// are subclasses of SubRegion. class SubRegion : public MemRegion { private: virtual void anchor(); protected: const MemRegion* superRegion; SubRegion(const MemRegion* sReg, Kind k) : MemRegion(k), superRegion(sReg) {} public: const MemRegion* getSuperRegion() const { return superRegion; } /// getExtent - Returns the size of the region in bytes. virtual DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const { return UnknownVal(); } MemRegionManager* getMemRegionManager() const override; bool isSubRegionOf(const MemRegion* R) const override; static bool classof(const MemRegion* R) { return R->getKind() > END_MEMSPACES; } }; //===----------------------------------------------------------------------===// // MemRegion subclasses. //===----------------------------------------------------------------------===// /// AllocaRegion - A region that represents an untyped blob of bytes created /// by a call to 'alloca'. class AllocaRegion : public SubRegion { friend class MemRegionManager; protected: unsigned Cnt; // Block counter. Used to distinguish different pieces of // memory allocated by alloca at the same call site. const Expr *Ex; AllocaRegion(const Expr *ex, unsigned cnt, const MemRegion *superRegion) : SubRegion(superRegion, AllocaRegionKind), Cnt(cnt), Ex(ex) {} public: const Expr *getExpr() const { return Ex; } bool isBoundable() const override { return true; } DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const override; void Profile(llvm::FoldingSetNodeID& ID) const override; static void ProfileRegion(llvm::FoldingSetNodeID& ID, const Expr *Ex, unsigned Cnt, const MemRegion *superRegion); void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion* R) { return R->getKind() == AllocaRegionKind; } }; /// TypedRegion - An abstract class representing regions that are typed. class TypedRegion : public SubRegion { public: void anchor() override; protected: TypedRegion(const MemRegion* sReg, Kind k) : SubRegion(sReg, k) {} public: virtual QualType getLocationType() const = 0; QualType getDesugaredLocationType(ASTContext &Context) const { return getLocationType().getDesugaredType(Context); } bool isBoundable() const override { return true; } static bool classof(const MemRegion* R) { unsigned k = R->getKind(); return k >= BEGIN_TYPED_REGIONS && k <= END_TYPED_REGIONS; } }; /// TypedValueRegion - An abstract class representing regions having a typed value. class TypedValueRegion : public TypedRegion { public: void anchor() override; protected: TypedValueRegion(const MemRegion* sReg, Kind k) : TypedRegion(sReg, k) {} public: virtual QualType getValueType() const = 0; QualType getLocationType() const override { // FIXME: We can possibly optimize this later to cache this value. QualType T = getValueType(); ASTContext &ctx = getContext(); if (T->getAs()) return ctx.getObjCObjectPointerType(T); return ctx.getPointerType(getValueType()); } QualType getDesugaredValueType(ASTContext &Context) const { QualType T = getValueType(); return T.getTypePtrOrNull() ? T.getDesugaredType(Context) : T; } DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const override; static bool classof(const MemRegion* R) { unsigned k = R->getKind(); return k >= BEGIN_TYPED_VALUE_REGIONS && k <= END_TYPED_VALUE_REGIONS; } }; class CodeTextRegion : public TypedRegion { public: void anchor() override; protected: CodeTextRegion(const MemRegion *sreg, Kind k) : TypedRegion(sreg, k) {} public: bool isBoundable() const override { return false; } static bool classof(const MemRegion* R) { Kind k = R->getKind(); return k >= BEGIN_CODE_TEXT_REGIONS && k <= END_CODE_TEXT_REGIONS; } }; /// FunctionCodeRegion - A region that represents code texts of function. class FunctionCodeRegion : public CodeTextRegion { const NamedDecl *FD; public: FunctionCodeRegion(const NamedDecl *fd, const MemRegion* sreg) : CodeTextRegion(sreg, FunctionCodeRegionKind), FD(fd) { assert(isa(fd) || isa(fd)); } QualType getLocationType() const override { const ASTContext &Ctx = getContext(); if (const FunctionDecl *D = dyn_cast(FD)) { return Ctx.getPointerType(D->getType()); } assert(isa(FD)); assert(false && "Getting the type of ObjCMethod is not supported yet"); // TODO: We might want to return a different type here (ex: id (*ty)(...)) // depending on how it is used. return QualType(); } const NamedDecl *getDecl() const { return FD; } void dumpToStream(raw_ostream &os) const override; void Profile(llvm::FoldingSetNodeID& ID) const override; static void ProfileRegion(llvm::FoldingSetNodeID& ID, const NamedDecl *FD, const MemRegion*); static bool classof(const MemRegion* R) { return R->getKind() == FunctionCodeRegionKind; } }; /// BlockCodeRegion - A region that represents code texts of blocks (closures). /// Blocks are represented with two kinds of regions. BlockCodeRegions /// represent the "code", while BlockDataRegions represent instances of blocks, /// which correspond to "code+data". The distinction is important, because /// like a closure a block captures the values of externally referenced /// variables. class BlockCodeRegion : public CodeTextRegion { friend class MemRegionManager; const BlockDecl *BD; AnalysisDeclContext *AC; CanQualType locTy; BlockCodeRegion(const BlockDecl *bd, CanQualType lTy, AnalysisDeclContext *ac, const MemRegion* sreg) : CodeTextRegion(sreg, BlockCodeRegionKind), BD(bd), AC(ac), locTy(lTy) {} public: QualType getLocationType() const override { return locTy; } const BlockDecl *getDecl() const { return BD; } AnalysisDeclContext *getAnalysisDeclContext() const { return AC; } void dumpToStream(raw_ostream &os) const override; void Profile(llvm::FoldingSetNodeID& ID) const override; static void ProfileRegion(llvm::FoldingSetNodeID& ID, const BlockDecl *BD, CanQualType, const AnalysisDeclContext*, const MemRegion*); static bool classof(const MemRegion* R) { return R->getKind() == BlockCodeRegionKind; } }; /// BlockDataRegion - A region that represents a block instance. /// Blocks are represented with two kinds of regions. BlockCodeRegions /// represent the "code", while BlockDataRegions represent instances of blocks, /// which correspond to "code+data". The distinction is important, because /// like a closure a block captures the values of externally referenced /// variables. class BlockDataRegion : public TypedRegion { friend class MemRegionManager; const BlockCodeRegion *BC; const LocationContext *LC; // Can be null */ unsigned BlockCount; void *ReferencedVars; void *OriginalVars; BlockDataRegion(const BlockCodeRegion *bc, const LocationContext *lc, unsigned count, const MemRegion *sreg) : TypedRegion(sreg, BlockDataRegionKind), BC(bc), LC(lc), BlockCount(count), ReferencedVars(nullptr), OriginalVars(nullptr) {} public: const BlockCodeRegion *getCodeRegion() const { return BC; } const BlockDecl *getDecl() const { return BC->getDecl(); } QualType getLocationType() const override { return BC->getLocationType(); } class referenced_vars_iterator { const MemRegion * const *R; const MemRegion * const *OriginalR; public: explicit referenced_vars_iterator(const MemRegion * const *r, const MemRegion * const *originalR) : R(r), OriginalR(originalR) {} const VarRegion *getCapturedRegion() const { return cast(*R); } const VarRegion *getOriginalRegion() const { return cast(*OriginalR); } bool operator==(const referenced_vars_iterator &I) const { assert((R == nullptr) == (I.R == nullptr)); return I.R == R; } bool operator!=(const referenced_vars_iterator &I) const { assert((R == nullptr) == (I.R == nullptr)); return I.R != R; } referenced_vars_iterator &operator++() { ++R; ++OriginalR; return *this; } }; /// Return the original region for a captured region, if /// one exists. const VarRegion *getOriginalRegion(const VarRegion *VR) const; referenced_vars_iterator referenced_vars_begin() const; referenced_vars_iterator referenced_vars_end() const; void dumpToStream(raw_ostream &os) const override; void Profile(llvm::FoldingSetNodeID& ID) const override; static void ProfileRegion(llvm::FoldingSetNodeID&, const BlockCodeRegion *, const LocationContext *, unsigned, const MemRegion *); static bool classof(const MemRegion* R) { return R->getKind() == BlockDataRegionKind; } private: void LazyInitializeReferencedVars(); std::pair getCaptureRegions(const VarDecl *VD); }; /// SymbolicRegion - A special, "non-concrete" region. Unlike other region /// classes, SymbolicRegion represents a region that serves as an alias for /// either a real region, a NULL pointer, etc. It essentially is used to /// map the concept of symbolic values into the domain of regions. Symbolic /// regions do not need to be typed. class SymbolicRegion : public SubRegion { protected: const SymbolRef sym; public: SymbolicRegion(const SymbolRef s, const MemRegion* sreg) : SubRegion(sreg, SymbolicRegionKind), sym(s) {} SymbolRef getSymbol() const { return sym; } bool isBoundable() const override { return true; } DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const override; void Profile(llvm::FoldingSetNodeID& ID) const override; static void ProfileRegion(llvm::FoldingSetNodeID& ID, SymbolRef sym, const MemRegion* superRegion); void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion* R) { return R->getKind() == SymbolicRegionKind; } }; /// StringRegion - Region associated with a StringLiteral. class StringRegion : public TypedValueRegion { friend class MemRegionManager; const StringLiteral* Str; protected: StringRegion(const StringLiteral* str, const MemRegion* sreg) : TypedValueRegion(sreg, StringRegionKind), Str(str) {} static void ProfileRegion(llvm::FoldingSetNodeID& ID, const StringLiteral* Str, const MemRegion* superRegion); public: const StringLiteral* getStringLiteral() const { return Str; } QualType getValueType() const override { return Str->getType(); } DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const override; bool isBoundable() const override { return false; } void Profile(llvm::FoldingSetNodeID& ID) const override { ProfileRegion(ID, Str, superRegion); } void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion* R) { return R->getKind() == StringRegionKind; } }; /// The region associated with an ObjCStringLiteral. class ObjCStringRegion : public TypedValueRegion { friend class MemRegionManager; const ObjCStringLiteral* Str; protected: ObjCStringRegion(const ObjCStringLiteral* str, const MemRegion* sreg) : TypedValueRegion(sreg, ObjCStringRegionKind), Str(str) {} static void ProfileRegion(llvm::FoldingSetNodeID& ID, const ObjCStringLiteral* Str, const MemRegion* superRegion); public: const ObjCStringLiteral* getObjCStringLiteral() const { return Str; } QualType getValueType() const override { return Str->getType(); } bool isBoundable() const override { return false; } void Profile(llvm::FoldingSetNodeID& ID) const override { ProfileRegion(ID, Str, superRegion); } void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion* R) { return R->getKind() == ObjCStringRegionKind; } }; /// CompoundLiteralRegion - A memory region representing a compound literal. /// Compound literals are essentially temporaries that are stack allocated /// or in the global constant pool. class CompoundLiteralRegion : public TypedValueRegion { private: friend class MemRegionManager; const CompoundLiteralExpr *CL; CompoundLiteralRegion(const CompoundLiteralExpr *cl, const MemRegion* sReg) : TypedValueRegion(sReg, CompoundLiteralRegionKind), CL(cl) {} static void ProfileRegion(llvm::FoldingSetNodeID& ID, const CompoundLiteralExpr *CL, const MemRegion* superRegion); public: QualType getValueType() const override { return CL->getType(); } bool isBoundable() const override { return !CL->isFileScope(); } void Profile(llvm::FoldingSetNodeID& ID) const override; void dumpToStream(raw_ostream &os) const override; const CompoundLiteralExpr *getLiteralExpr() const { return CL; } static bool classof(const MemRegion* R) { return R->getKind() == CompoundLiteralRegionKind; } }; class DeclRegion : public TypedValueRegion { protected: const Decl *D; DeclRegion(const Decl *d, const MemRegion* sReg, Kind k) : TypedValueRegion(sReg, k), D(d) {} static void ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl *D, const MemRegion* superRegion, Kind k); public: const Decl *getDecl() const { return D; } void Profile(llvm::FoldingSetNodeID& ID) const override; static bool classof(const MemRegion* R) { unsigned k = R->getKind(); return k >= BEGIN_DECL_REGIONS && k <= END_DECL_REGIONS; } }; class VarRegion : public DeclRegion { friend class MemRegionManager; // Constructors and private methods. VarRegion(const VarDecl *vd, const MemRegion* sReg) : DeclRegion(vd, sReg, VarRegionKind) {} static void ProfileRegion(llvm::FoldingSetNodeID& ID, const VarDecl *VD, const MemRegion *superRegion) { DeclRegion::ProfileRegion(ID, VD, superRegion, VarRegionKind); } void Profile(llvm::FoldingSetNodeID& ID) const override; public: const VarDecl *getDecl() const { return cast(D); } const StackFrameContext *getStackFrame() const; QualType getValueType() const override { // FIXME: We can cache this if needed. return getDecl()->getType(); } void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion* R) { return R->getKind() == VarRegionKind; } bool canPrintPrettyAsExpr() const override; void printPrettyAsExpr(raw_ostream &os) const override; }; /// CXXThisRegion - Represents the region for the implicit 'this' parameter /// in a call to a C++ method. This region doesn't represent the object /// referred to by 'this', but rather 'this' itself. class CXXThisRegion : public TypedValueRegion { friend class MemRegionManager; CXXThisRegion(const PointerType *thisPointerTy, const MemRegion *sReg) : TypedValueRegion(sReg, CXXThisRegionKind), ThisPointerTy(thisPointerTy) {} static void ProfileRegion(llvm::FoldingSetNodeID &ID, const PointerType *PT, const MemRegion *sReg); void Profile(llvm::FoldingSetNodeID &ID) const override; public: QualType getValueType() const override { return QualType(ThisPointerTy, 0); } void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion* R) { return R->getKind() == CXXThisRegionKind; } private: const PointerType *ThisPointerTy; }; class FieldRegion : public DeclRegion { friend class MemRegionManager; FieldRegion(const FieldDecl *fd, const MemRegion* sReg) : DeclRegion(fd, sReg, FieldRegionKind) {} public: const FieldDecl *getDecl() const { return cast(D); } QualType getValueType() const override { // FIXME: We can cache this if needed. return getDecl()->getType(); } DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const override; static void ProfileRegion(llvm::FoldingSetNodeID& ID, const FieldDecl *FD, const MemRegion* superRegion) { DeclRegion::ProfileRegion(ID, FD, superRegion, FieldRegionKind); } static bool classof(const MemRegion* R) { return R->getKind() == FieldRegionKind; } void dumpToStream(raw_ostream &os) const override; bool canPrintPretty() const override; void printPretty(raw_ostream &os) const override; bool canPrintPrettyAsExpr() const override; void printPrettyAsExpr(raw_ostream &os) const override; }; class ObjCIvarRegion : public DeclRegion { friend class MemRegionManager; ObjCIvarRegion(const ObjCIvarDecl *ivd, const MemRegion* sReg); static void ProfileRegion(llvm::FoldingSetNodeID& ID, const ObjCIvarDecl *ivd, const MemRegion* superRegion); public: const ObjCIvarDecl *getDecl() const; QualType getValueType() const override; bool canPrintPrettyAsExpr() const override; void printPrettyAsExpr(raw_ostream &os) const override; void dumpToStream(raw_ostream &os) const override; static bool classof(const MemRegion* R) { return R->getKind() == ObjCIvarRegionKind; } }; //===----------------------------------------------------------------------===// // Auxiliary data classes for use with MemRegions. //===----------------------------------------------------------------------===// class ElementRegion; class RegionRawOffset { private: friend class ElementRegion; const MemRegion *Region; CharUnits Offset; RegionRawOffset(const MemRegion* reg, CharUnits offset = CharUnits::Zero()) : Region(reg), Offset(offset) {} public: // FIXME: Eventually support symbolic offsets. CharUnits getOffset() const { return Offset; } const MemRegion *getRegion() const { return Region; } void dumpToStream(raw_ostream &os) const; void dump() const; }; /// \brief ElementRegin is used to represent both array elements and casts. class ElementRegion : public TypedValueRegion { friend class MemRegionManager; QualType ElementType; NonLoc Index; ElementRegion(QualType elementType, NonLoc Idx, const MemRegion* sReg) : TypedValueRegion(sReg, ElementRegionKind), ElementType(elementType), Index(Idx) { assert((!Idx.getAs() || Idx.castAs().getValue().isSigned()) && "The index must be signed"); } static void ProfileRegion(llvm::FoldingSetNodeID& ID, QualType elementType, SVal Idx, const MemRegion* superRegion); public: NonLoc getIndex() const { return Index; } QualType getValueType() const override { return ElementType; } QualType getElementType() const { return ElementType; } /// Compute the offset within the array. The array might also be a subobject. RegionRawOffset getAsArrayOffset() const; void dumpToStream(raw_ostream &os) const override; void Profile(llvm::FoldingSetNodeID& ID) const override; static bool classof(const MemRegion* R) { return R->getKind() == ElementRegionKind; } }; // C++ temporary object associated with an expression. class CXXTempObjectRegion : public TypedValueRegion { friend class MemRegionManager; Expr const *Ex; CXXTempObjectRegion(Expr const *E, MemRegion const *sReg) : TypedValueRegion(sReg, CXXTempObjectRegionKind), Ex(E) {} static void ProfileRegion(llvm::FoldingSetNodeID &ID, Expr const *E, const MemRegion *sReg); public: const Expr *getExpr() const { return Ex; } QualType getValueType() const override { return Ex->getType(); } void dumpToStream(raw_ostream &os) const override; void Profile(llvm::FoldingSetNodeID &ID) const override; static bool classof(const MemRegion* R) { return R->getKind() == CXXTempObjectRegionKind; } }; // CXXBaseObjectRegion represents a base object within a C++ object. It is // identified by the base class declaration and the region of its parent object. class CXXBaseObjectRegion : public TypedValueRegion { friend class MemRegionManager; llvm::PointerIntPair Data; CXXBaseObjectRegion(const CXXRecordDecl *RD, bool IsVirtual, const MemRegion *SReg) : TypedValueRegion(SReg, CXXBaseObjectRegionKind), Data(RD, IsVirtual) {} static void ProfileRegion(llvm::FoldingSetNodeID &ID, const CXXRecordDecl *RD, bool IsVirtual, const MemRegion *SReg); public: const CXXRecordDecl *getDecl() const { return Data.getPointer(); } bool isVirtual() const { return Data.getInt(); } QualType getValueType() const override; void dumpToStream(raw_ostream &os) const override; void Profile(llvm::FoldingSetNodeID &ID) const override; static bool classof(const MemRegion *region) { return region->getKind() == CXXBaseObjectRegionKind; } bool canPrintPrettyAsExpr() const override; void printPrettyAsExpr(raw_ostream &os) const override; }; template const RegionTy* MemRegion::getAs() const { if (const RegionTy* RT = dyn_cast(this)) return RT; return nullptr; } //===----------------------------------------------------------------------===// // MemRegionManager - Factory object for creating regions. //===----------------------------------------------------------------------===// class MemRegionManager { ASTContext &C; llvm::BumpPtrAllocator& A; llvm::FoldingSet Regions; GlobalInternalSpaceRegion *InternalGlobals; GlobalSystemSpaceRegion *SystemGlobals; GlobalImmutableSpaceRegion *ImmutableGlobals; llvm::DenseMap StackLocalsSpaceRegions; llvm::DenseMap StackArgumentsSpaceRegions; llvm::DenseMap StaticsGlobalSpaceRegions; HeapSpaceRegion *heap; UnknownSpaceRegion *unknown; CodeSpaceRegion *code; public: MemRegionManager(ASTContext &c, llvm::BumpPtrAllocator &a) : C(c), A(a), InternalGlobals(nullptr), SystemGlobals(nullptr), ImmutableGlobals(nullptr), heap(nullptr), unknown(nullptr), code(nullptr) {} ~MemRegionManager(); ASTContext &getContext() { return C; } llvm::BumpPtrAllocator &getAllocator() { return A; } /// getStackLocalsRegion - Retrieve the memory region associated with the /// specified stack frame. const StackLocalsSpaceRegion * getStackLocalsRegion(const StackFrameContext *STC); /// getStackArgumentsRegion - Retrieve the memory region associated with /// function/method arguments of the specified stack frame. const StackArgumentsSpaceRegion * getStackArgumentsRegion(const StackFrameContext *STC); /// getGlobalsRegion - Retrieve the memory region associated with /// global variables. const GlobalsSpaceRegion *getGlobalsRegion( MemRegion::Kind K = MemRegion::GlobalInternalSpaceRegionKind, const CodeTextRegion *R = nullptr); /// getHeapRegion - Retrieve the memory region associated with the /// generic "heap". const HeapSpaceRegion *getHeapRegion(); /// getUnknownRegion - Retrieve the memory region associated with unknown /// memory space. const UnknownSpaceRegion *getUnknownRegion(); const CodeSpaceRegion *getCodeRegion(); /// getAllocaRegion - Retrieve a region associated with a call to alloca(). const AllocaRegion *getAllocaRegion(const Expr *Ex, unsigned Cnt, const LocationContext *LC); /// getCompoundLiteralRegion - Retrieve the region associated with a /// given CompoundLiteral. const CompoundLiteralRegion* getCompoundLiteralRegion(const CompoundLiteralExpr *CL, const LocationContext *LC); /// getCXXThisRegion - Retrieve the [artificial] region associated with the /// parameter 'this'. const CXXThisRegion *getCXXThisRegion(QualType thisPointerTy, const LocationContext *LC); /// \brief Retrieve or create a "symbolic" memory region. const SymbolicRegion* getSymbolicRegion(SymbolRef Sym); /// \brief Return a unique symbolic region belonging to heap memory space. const SymbolicRegion *getSymbolicHeapRegion(SymbolRef sym); const StringRegion *getStringRegion(const StringLiteral* Str); const ObjCStringRegion *getObjCStringRegion(const ObjCStringLiteral *Str); /// getVarRegion - Retrieve or create the memory region associated with /// a specified VarDecl and LocationContext. const VarRegion* getVarRegion(const VarDecl *D, const LocationContext *LC); /// getVarRegion - Retrieve or create the memory region associated with /// a specified VarDecl and super region. const VarRegion* getVarRegion(const VarDecl *D, const MemRegion *superR); /// getElementRegion - Retrieve the memory region associated with the /// associated element type, index, and super region. const ElementRegion *getElementRegion(QualType elementType, NonLoc Idx, const MemRegion *superRegion, ASTContext &Ctx); const ElementRegion *getElementRegionWithSuper(const ElementRegion *ER, const MemRegion *superRegion) { return getElementRegion(ER->getElementType(), ER->getIndex(), superRegion, ER->getContext()); } /// getFieldRegion - Retrieve or create the memory region associated with /// a specified FieldDecl. 'superRegion' corresponds to the containing /// memory region (which typically represents the memory representing /// a structure or class). const FieldRegion *getFieldRegion(const FieldDecl *fd, const MemRegion* superRegion); const FieldRegion *getFieldRegionWithSuper(const FieldRegion *FR, const MemRegion *superRegion) { return getFieldRegion(FR->getDecl(), superRegion); } /// getObjCIvarRegion - Retrieve or create the memory region associated with /// a specified Objective-c instance variable. 'superRegion' corresponds /// to the containing region (which typically represents the Objective-C /// object). const ObjCIvarRegion *getObjCIvarRegion(const ObjCIvarDecl *ivd, const MemRegion* superRegion); const CXXTempObjectRegion *getCXXTempObjectRegion(Expr const *Ex, LocationContext const *LC); /// Create a CXXBaseObjectRegion with the given base class for region /// \p Super. /// /// The type of \p Super is assumed be a class deriving from \p BaseClass. const CXXBaseObjectRegion * getCXXBaseObjectRegion(const CXXRecordDecl *BaseClass, const MemRegion *Super, bool IsVirtual); /// Create a CXXBaseObjectRegion with the same CXXRecordDecl but a different /// super region. const CXXBaseObjectRegion * getCXXBaseObjectRegionWithSuper(const CXXBaseObjectRegion *baseReg, const MemRegion *superRegion) { return getCXXBaseObjectRegion(baseReg->getDecl(), superRegion, baseReg->isVirtual()); } const FunctionCodeRegion *getFunctionCodeRegion(const NamedDecl *FD); const BlockCodeRegion *getBlockCodeRegion(const BlockDecl *BD, CanQualType locTy, AnalysisDeclContext *AC); /// getBlockDataRegion - Get the memory region associated with an instance /// of a block. Unlike many other MemRegions, the LocationContext* /// argument is allowed to be NULL for cases where we have no known /// context. const BlockDataRegion *getBlockDataRegion(const BlockCodeRegion *bc, const LocationContext *lc, unsigned blockCount); /// Create a CXXTempObjectRegion for temporaries which are lifetime-extended /// by static references. This differs from getCXXTempObjectRegion in the /// super-region used. const CXXTempObjectRegion *getCXXStaticTempObjectRegion(const Expr *Ex); private: template RegionTy* getRegion(const A1 a1); template RegionTy* getSubRegion(const A1 a1, const MemRegion* superRegion); template RegionTy* getRegion(const A1 a1, const A2 a2); template RegionTy* getSubRegion(const A1 a1, const A2 a2, const MemRegion* superRegion); template RegionTy* getSubRegion(const A1 a1, const A2 a2, const A3 a3, const MemRegion* superRegion); template const REG* LazyAllocate(REG*& region); template const REG* LazyAllocate(REG*& region, ARG a); }; //===----------------------------------------------------------------------===// // Out-of-line member definitions. //===----------------------------------------------------------------------===// inline ASTContext &MemRegion::getContext() const { return getMemRegionManager()->getContext(); } //===----------------------------------------------------------------------===// // Means for storing region/symbol handling traits. //===----------------------------------------------------------------------===// /// Information about invalidation for a particular region/symbol. class RegionAndSymbolInvalidationTraits { typedef unsigned char StorageTypeForKinds; llvm::DenseMap MRTraitsMap; llvm::DenseMap SymTraitsMap; typedef llvm::DenseMap::const_iterator const_region_iterator; typedef llvm::DenseMap::const_iterator const_symbol_iterator; public: /// \brief Describes different invalidation traits. enum InvalidationKinds { /// Tells that a region's contents is not changed. TK_PreserveContents = 0x1, /// Suppress pointer-escaping of a region. TK_SuppressEscape = 0x2, // Do not invalidate super region. TK_DoNotInvalidateSuperRegion = 0x4, /// When applied to a MemSpaceRegion, indicates the entire memory space /// should be invalidated. TK_EntireMemSpace = 0x8 // Do not forget to extend StorageTypeForKinds if number of traits exceed // the number of bits StorageTypeForKinds can store. }; void setTrait(SymbolRef Sym, InvalidationKinds IK); void setTrait(const MemRegion *MR, InvalidationKinds IK); bool hasTrait(SymbolRef Sym, InvalidationKinds IK) const; bool hasTrait(const MemRegion *MR, InvalidationKinds IK) const; }; } // end GR namespace } // end clang namespace //===----------------------------------------------------------------------===// // Pretty-printing regions. //===----------------------------------------------------------------------===// namespace llvm { static inline raw_ostream &operator<<(raw_ostream &os, const clang::ento::MemRegion* R) { R->dumpToStream(os); return os; } } // end llvm namespace #endif diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt index 479030d88990..62ccc3cb4970 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -1,89 +1,92 @@ set(LLVM_LINK_COMPONENTS Support ) add_clang_library(clangStaticAnalyzerCheckers AllocationDiagnostics.cpp AnalyzerStatsChecker.cpp ArrayBoundChecker.cpp ArrayBoundCheckerV2.cpp BasicObjCFoundationChecks.cpp BoolAssignmentChecker.cpp BuiltinFunctionChecker.cpp CStringChecker.cpp CStringSyntaxChecker.cpp CallAndMessageChecker.cpp CastSizeChecker.cpp CastToStructChecker.cpp CheckObjCDealloc.cpp CheckObjCInstMethSignature.cpp CheckSecuritySyntaxOnly.cpp CheckSizeofPointer.cpp CheckerDocumentation.cpp ChrootChecker.cpp ClangCheckers.cpp DeadStoresChecker.cpp DebugCheckers.cpp DereferenceChecker.cpp DirectIvarAssignment.cpp DivZeroChecker.cpp DynamicTypePropagation.cpp DynamicTypeChecker.cpp ExprInspectionChecker.cpp FixedAddressChecker.cpp GenericTaintChecker.cpp IdenticalExprChecker.cpp IvarInvalidationChecker.cpp LLVMConventionsChecker.cpp LocalizationChecker.cpp MacOSKeychainAPIChecker.cpp MacOSXAPIChecker.cpp MallocChecker.cpp MallocOverflowSecurityChecker.cpp MallocSizeofChecker.cpp + MPI-Checker/MPIBugReporter.cpp + MPI-Checker/MPIChecker.cpp + MPI-Checker/MPIFunctionClassifier.cpp NSAutoreleasePoolChecker.cpp NSErrorChecker.cpp NoReturnFunctionChecker.cpp NonNullParamChecker.cpp NullabilityChecker.cpp ObjCAtSyncChecker.cpp ObjCContainersASTChecker.cpp ObjCContainersChecker.cpp ObjCMissingSuperCallChecker.cpp ObjCSelfInitChecker.cpp ObjCSuperDeallocChecker.cpp ObjCUnusedIVarsChecker.cpp PaddingChecker.cpp PointerArithChecker.cpp PointerSubChecker.cpp PthreadLockChecker.cpp RetainCountChecker.cpp ReturnPointerRangeChecker.cpp ReturnUndefChecker.cpp SimpleStreamChecker.cpp StackAddrEscapeChecker.cpp StreamChecker.cpp TaintTesterChecker.cpp TestAfterDivZeroChecker.cpp TraversalChecker.cpp UndefBranchChecker.cpp UndefCapturedBlockVarChecker.cpp UndefResultChecker.cpp UndefinedArraySubscriptChecker.cpp UndefinedAssignmentChecker.cpp UnixAPIChecker.cpp UnreachableCodeChecker.cpp VforkChecker.cpp VLASizeChecker.cpp VirtualCallChecker.cpp DEPENDS ClangSACheckers LINK_LIBS clangAST clangAnalysis clangBasic clangLex clangStaticAnalyzerCore ) diff --git a/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp new file mode 100644 index 000000000000..d56ea6d689d3 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp @@ -0,0 +1,115 @@ +//===-- MPIBugReporter.cpp - bug reporter -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines prefabricated reports which are emitted in +/// case of MPI related bugs, detected by path-sensitive analysis. +/// +//===----------------------------------------------------------------------===// + +#include "MPIBugReporter.h" +#include "MPIChecker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" + +namespace clang { +namespace ento { +namespace mpi { + +void MPIBugReporter::reportDoubleNonblocking( + const CallEvent &MPICallEvent, const ento::mpi::Request &Req, + const MemRegion *const RequestRegion, + const ExplodedNode *const ExplNode, + BugReporter &BReporter) const { + + std::string ErrorText; + ErrorText = "Double nonblocking on request " + + RequestRegion->getDescriptiveName() + ". "; + + auto Report = llvm::make_unique(*DoubleNonblockingBugType, + ErrorText, ExplNode); + + Report->addRange(MPICallEvent.getSourceRange()); + SourceRange Range = RequestRegion->sourceRange(); + + if (Range.isValid()) + Report->addRange(Range); + + Report->addVisitor(llvm::make_unique( + RequestRegion, "Request is previously used by nonblocking call here. ")); + Report->markInteresting(RequestRegion); + + BReporter.emitReport(std::move(Report)); +} + +void MPIBugReporter::reportMissingWait( + const ento::mpi::Request &Req, const MemRegion *const RequestRegion, + const ExplodedNode *const ExplNode, + BugReporter &BReporter) const { + std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() + + " has no matching wait. "}; + + auto Report = + llvm::make_unique(*MissingWaitBugType, ErrorText, ExplNode); + + SourceRange Range = RequestRegion->sourceRange(); + if (Range.isValid()) + Report->addRange(Range); + Report->addVisitor(llvm::make_unique( + RequestRegion, "Request is previously used by nonblocking call here. ")); + Report->markInteresting(RequestRegion); + + BReporter.emitReport(std::move(Report)); +} + +void MPIBugReporter::reportUnmatchedWait( + const CallEvent &CE, const clang::ento::MemRegion *const RequestRegion, + const ExplodedNode *const ExplNode, + BugReporter &BReporter) const { + std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() + + " has no matching nonblocking call. "}; + + auto Report = + llvm::make_unique(*UnmatchedWaitBugType, ErrorText, ExplNode); + + Report->addRange(CE.getSourceRange()); + SourceRange Range = RequestRegion->sourceRange(); + if (Range.isValid()) + Report->addRange(Range); + + BReporter.emitReport(std::move(Report)); +} + +PathDiagnosticPiece *MPIBugReporter::RequestNodeVisitor::VisitNode( + const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, + BugReport &BR) { + + if (IsNodeFound) + return nullptr; + + const Request *const Req = N->getState()->get(RequestRegion); + const Request *const PrevReq = + PrevN->getState()->get(RequestRegion); + + // Check if request was previously unused or in a different state. + if ((Req && !PrevReq) || (Req->CurrentState != PrevReq->CurrentState)) { + IsNodeFound = true; + + ProgramPoint P = PrevN->getLocation(); + PathDiagnosticLocation L = + PathDiagnosticLocation::create(P, BRC.getSourceManager()); + + return new PathDiagnosticEventPiece(L, ErrorText); + } + + return nullptr; +} + +} // end of namespace: mpi +} // end of namespace: ento +} // end of namespace: clang diff --git a/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h new file mode 100644 index 000000000000..edccad3523a4 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h @@ -0,0 +1,111 @@ +//===-- MPIBugReporter.h - bug reporter -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines prefabricated reports which are emitted in +/// case of MPI related bugs, detected by path-sensitive analysis. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPIBUGREPORTER_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPIBUGREPORTER_H + +#include "MPITypes.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" + +namespace clang { +namespace ento { +namespace mpi { + +class MPIBugReporter { +public: + MPIBugReporter(const CheckerBase &CB) { + UnmatchedWaitBugType.reset(new BugType(&CB, "Unmatched wait", MPIError)); + DoubleNonblockingBugType.reset( + new BugType(&CB, "Double nonblocking", MPIError)); + MissingWaitBugType.reset(new BugType(&CB, "Missing wait", MPIError)); + } + + /// Report duplicate request use by nonblocking calls without intermediate + /// wait. + /// + /// \param MPICallEvent MPI call that caused the double nonblocking + /// \param Req request that was used by two nonblocking calls in sequence + /// \param RequestRegion memory region of the request + /// \param ExplNode node in the graph the bug appeared at + /// \param BReporter bug reporter for current context + void reportDoubleNonblocking(const CallEvent &MPICallEvent, + const Request &Req, + const MemRegion *const RequestRegion, + const ExplodedNode *const ExplNode, + BugReporter &BReporter) const; + + /// Report a missing wait for a nonblocking call. A missing wait report + /// is emitted if a nonblocking call is not matched in the scope of a + /// function. + /// + /// \param Req request that is not matched by a wait + /// \param RequestRegion memory region of the request + /// \param ExplNode node in the graph the bug appeared at + /// \param BReporter bug reporter for current context + void reportMissingWait(const Request &Req, + const MemRegion *const RequestRegion, + const ExplodedNode *const ExplNode, + BugReporter &BReporter) const; + + /// Report a wait on a request that has not been used at all before. + /// + /// \param CE wait call that uses the request + /// \param ReqRegion memory region of the request + /// \param ExplNode node in the graph the bug appeared at + /// \param BReporter bug reporter for current context + void reportUnmatchedWait(const CallEvent &CE, + const MemRegion *const RequestRegion, + const ExplodedNode *const ExplNode, + BugReporter &BReporter) const; + +private: + const std::string MPIError{"MPI Error"}; + + // path-sensitive bug types + std::unique_ptr UnmatchedWaitBugType; + std::unique_ptr MissingWaitBugType; + std::unique_ptr DoubleNonblockingBugType; + + /// Bug visitor class to find the node where the request region was previously + /// used in order to include it into the BugReport path. + class RequestNodeVisitor : public BugReporterVisitorImpl { + public: + RequestNodeVisitor(const MemRegion *const MemoryRegion, + const std::string &ErrText) + : RequestRegion(MemoryRegion), ErrorText{ErrText} {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int X = 0; + ID.AddPointer(&X); + ID.AddPointer(RequestRegion); + } + + PathDiagnosticPiece *VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) override; + + private: + const MemRegion *const RequestRegion; + bool IsNodeFound{false}; + std::string ErrorText; + }; +}; + +} // end of namespace: mpi +} // end of namespace: ento +} // end of namespace: clang + +#endif diff --git a/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp new file mode 100644 index 000000000000..c3d0f8f2a129 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp @@ -0,0 +1,190 @@ +//===-- MPIChecker.cpp - Checker Entry Point Class --------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines the main class of MPI-Checker which serves as an entry +/// point. It is created once for each translation unit analysed. +/// The checker defines path-sensitive checks, to verify correct usage of the +/// MPI API. +/// +//===----------------------------------------------------------------------===// + +#include "MPIChecker.h" +#include "../ClangSACheckers.h" + +namespace clang { +namespace ento { +namespace mpi { + +void MPIChecker::checkDoubleNonblocking(const CallEvent &PreCallEvent, + CheckerContext &Ctx) const { + if (!FuncClassifier->isNonBlockingType(PreCallEvent.getCalleeIdentifier())) { + return; + } + const MemRegion *const MR = + PreCallEvent.getArgSVal(PreCallEvent.getNumArgs() - 1).getAsRegion(); + if (!MR) + return; + const ElementRegion *const ER = dyn_cast(MR); + + // The region must be typed, in order to reason about it. + if (!isa(MR) || (ER && !isa(ER->getSuperRegion()))) + return; + + ProgramStateRef State = Ctx.getState(); + const Request *const Req = State->get(MR); + + // double nonblocking detected + if (Req && Req->CurrentState == Request::State::Nonblocking) { + ExplodedNode *ErrorNode = Ctx.generateNonFatalErrorNode(); + BReporter.reportDoubleNonblocking(PreCallEvent, *Req, MR, ErrorNode, Ctx.getBugReporter()); + Ctx.addTransition(ErrorNode->getState(), ErrorNode); + } + // no error + else { + State = State->set(MR, Request::State::Nonblocking); + Ctx.addTransition(State); + } +} + +void MPIChecker::checkUnmatchedWaits(const CallEvent &PreCallEvent, + CheckerContext &Ctx) const { + if (!FuncClassifier->isWaitType(PreCallEvent.getCalleeIdentifier())) + return; + const MemRegion *const MR = topRegionUsedByWait(PreCallEvent); + if (!MR) + return; + const ElementRegion *const ER = dyn_cast(MR); + + // The region must be typed, in order to reason about it. + if (!isa(MR) || (ER && !isa(ER->getSuperRegion()))) + return; + + llvm::SmallVector ReqRegions; + allRegionsUsedByWait(ReqRegions, MR, PreCallEvent, Ctx); + if (ReqRegions.empty()) + return; + + ProgramStateRef State = Ctx.getState(); + static CheckerProgramPointTag Tag("MPI-Checker", "UnmatchedWait"); + ExplodedNode *ErrorNode{nullptr}; + + // Check all request regions used by the wait function. + for (const auto &ReqRegion : ReqRegions) { + const Request *const Req = State->get(ReqRegion); + State = State->set(ReqRegion, Request::State::Wait); + if (!Req) { + if (!ErrorNode) { + ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag); + State = ErrorNode->getState(); + } + // A wait has no matching nonblocking call. + BReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ErrorNode, Ctx.getBugReporter()); + } + } + + if (!ErrorNode) { + Ctx.addTransition(State); + } else { + Ctx.addTransition(State, ErrorNode); + } +} + +void MPIChecker::checkMissingWaits(SymbolReaper &SymReaper, + CheckerContext &Ctx) const { + if (!SymReaper.hasDeadSymbols()) + return; + + ProgramStateRef State = Ctx.getState(); + const auto &Requests = State->get(); + if (Requests.isEmpty()) + return; + + static CheckerProgramPointTag Tag("MPI-Checker", "MissingWait"); + ExplodedNode *ErrorNode{nullptr}; + + auto ReqMap = State->get(); + for (const auto &Req : ReqMap) { + if (!SymReaper.isLiveRegion(Req.first)) { + if (Req.second.CurrentState == Request::State::Nonblocking) { + + if (!ErrorNode) { + ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag); + State = ErrorNode->getState(); + } + BReporter.reportMissingWait(Req.second, Req.first, ErrorNode, Ctx.getBugReporter()); + } + State = State->remove(Req.first); + } + } + + // Transition to update the state regarding removed requests. + if (!ErrorNode) { + Ctx.addTransition(State); + } else { + Ctx.addTransition(State, ErrorNode); + } +} + +const MemRegion *MPIChecker::topRegionUsedByWait(const CallEvent &CE) const { + + if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) { + return CE.getArgSVal(0).getAsRegion(); + } else if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) { + return CE.getArgSVal(1).getAsRegion(); + } else { + return (const MemRegion *)nullptr; + } +} + +void MPIChecker::allRegionsUsedByWait( + llvm::SmallVector &ReqRegions, + const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const { + + MemRegionManager *const RegionManager = MR->getMemRegionManager(); + + if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) { + const MemRegion *SuperRegion{nullptr}; + if (const ElementRegion *const ER = MR->getAs()) { + SuperRegion = ER->getSuperRegion(); + } + + // A single request is passed to MPI_Waitall. + if (!SuperRegion) { + ReqRegions.push_back(MR); + return; + } + + const auto &Size = Ctx.getStoreManager().getSizeInElements( + Ctx.getState(), SuperRegion, + CE.getArgExpr(1)->getType()->getPointeeType()); + const llvm::APSInt &ArrSize = Size.getAs()->getValue(); + + for (size_t i = 0; i < ArrSize; ++i) { + const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i); + + const ElementRegion *const ER = RegionManager->getElementRegion( + CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion, + Ctx.getASTContext()); + + ReqRegions.push_back(ER->getAs()); + } + } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) { + ReqRegions.push_back(MR); + } +} + +} // end of namespace: mpi +} // end of namespace: ento +} // end of namespace: clang + +// Registers the checker for static analysis. +void clang::ento::registerMPIChecker(CheckerManager &MGR) { + MGR.registerChecker(); +} diff --git a/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h new file mode 100644 index 000000000000..20c60ad076a2 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h @@ -0,0 +1,107 @@ +//===-- MPIChecker.h - Verify MPI API usage- --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines the main class of MPI-Checker which serves as an entry +/// point. It is created once for each translation unit analysed. +/// The checker defines path-sensitive checks, to verify correct usage of the +/// MPI API. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPICHECKER_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPICHECKER_H + +#include "MPIBugReporter.h" +#include "MPIFunctionClassifier.h" +#include "MPITypes.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +namespace clang { +namespace ento { +namespace mpi { + +class MPIChecker : public Checker { +public: + MPIChecker() : BReporter(*this) { } + + // path-sensitive callbacks + void checkPreCall(const CallEvent &CE, CheckerContext &Ctx) const { + dynamicInit(Ctx); + checkUnmatchedWaits(CE, Ctx); + checkDoubleNonblocking(CE, Ctx); + } + + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &Ctx) const { + dynamicInit(Ctx); + checkMissingWaits(SymReaper, Ctx); + } + + void dynamicInit(CheckerContext &Ctx) const { + if (FuncClassifier) + return; + const_cast &>(FuncClassifier) + .reset(new MPIFunctionClassifier{Ctx.getASTContext()}); + + } + + /// Checks if a request is used by nonblocking calls multiple times + /// in sequence without intermediate wait. The check contains a guard, + /// in order to only inspect nonblocking functions. + /// + /// \param PreCallEvent MPI call to verify + void checkDoubleNonblocking(const clang::ento::CallEvent &PreCallEvent, + clang::ento::CheckerContext &Ctx) const; + + /// Checks if a request is used by a wait multiple times in sequence without + /// intermediate nonblocking call or if the request used by the wait + /// function was not used at all before. The check contains a guard, + /// in order to only inspect wait functions. + /// + /// \param PreCallEvent MPI call to verify + void checkUnmatchedWaits(const clang::ento::CallEvent &PreCallEvent, + clang::ento::CheckerContext &Ctx) const; + + /// Check if a nonblocking call is not matched by a wait. + /// If a memory region is not alive and the last function using the + /// request was a nonblocking call, this is rated as a missing wait. + void checkMissingWaits(clang::ento::SymbolReaper &SymReaper, + clang::ento::CheckerContext &Ctx) const; + +private: + /// Collects all memory regions of a request(array) used by a wait + /// function. If the wait function uses a single request, this is a single + /// region. For wait functions using multiple requests, multiple regions + /// representing elements in the array are collected. + /// + /// \param ReqRegions vector the regions get pushed into + /// \param MR top most region to iterate + /// \param CE MPI wait call using the request(s) + void allRegionsUsedByWait( + llvm::SmallVector &ReqRegions, + const clang::ento::MemRegion *const MR, const clang::ento::CallEvent &CE, + clang::ento::CheckerContext &Ctx) const; + + /// Returns the memory region used by a wait function. + /// Distinguishes between MPI_Wait and MPI_Waitall. + /// + /// \param CE MPI wait call + const clang::ento::MemRegion * + topRegionUsedByWait(const clang::ento::CallEvent &CE) const; + + const std::unique_ptr FuncClassifier; + MPIBugReporter BReporter; +}; + +} // end of namespace: mpi +} // end of namespace: ento +} // end of namespace: clang + +#endif diff --git a/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp new file mode 100644 index 000000000000..ad937f683d30 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp @@ -0,0 +1,284 @@ +//===-- MPIFunctionClassifier.cpp - classifies MPI functions ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines functionality to identify and classify MPI functions. +/// +//===----------------------------------------------------------------------===// + +#include "MPIFunctionClassifier.h" +#include "llvm/ADT/STLExtras.h" + +namespace clang { +namespace ento { +namespace mpi { + +void MPIFunctionClassifier::identifierInit(ASTContext &ASTCtx) { + // Initialize function identifiers. + initPointToPointIdentifiers(ASTCtx); + initCollectiveIdentifiers(ASTCtx); + initAdditionalIdentifiers(ASTCtx); +} + +void MPIFunctionClassifier::initPointToPointIdentifiers(ASTContext &ASTCtx) { + // Copy identifiers into the correct classification containers. + IdentInfo_MPI_Send = &ASTCtx.Idents.get("MPI_Send"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Send); + MPIType.push_back(IdentInfo_MPI_Send); + assert(IdentInfo_MPI_Send); + + IdentInfo_MPI_Isend = &ASTCtx.Idents.get("MPI_Isend"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Isend); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Isend); + MPIType.push_back(IdentInfo_MPI_Isend); + assert(IdentInfo_MPI_Isend); + + IdentInfo_MPI_Ssend = &ASTCtx.Idents.get("MPI_Ssend"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Ssend); + MPIType.push_back(IdentInfo_MPI_Ssend); + assert(IdentInfo_MPI_Ssend); + + IdentInfo_MPI_Issend = &ASTCtx.Idents.get("MPI_Issend"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Issend); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Issend); + MPIType.push_back(IdentInfo_MPI_Issend); + assert(IdentInfo_MPI_Issend); + + IdentInfo_MPI_Bsend = &ASTCtx.Idents.get("MPI_Bsend"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Bsend); + MPIType.push_back(IdentInfo_MPI_Bsend); + assert(IdentInfo_MPI_Bsend); + + IdentInfo_MPI_Ibsend = &ASTCtx.Idents.get("MPI_Ibsend"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Ibsend); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Ibsend); + MPIType.push_back(IdentInfo_MPI_Ibsend); + assert(IdentInfo_MPI_Ibsend); + + IdentInfo_MPI_Rsend = &ASTCtx.Idents.get("MPI_Rsend"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Rsend); + MPIType.push_back(IdentInfo_MPI_Rsend); + assert(IdentInfo_MPI_Rsend); + + IdentInfo_MPI_Irsend = &ASTCtx.Idents.get("MPI_Irsend"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Irsend); + MPIType.push_back(IdentInfo_MPI_Irsend); + assert(IdentInfo_MPI_Irsend); + + IdentInfo_MPI_Recv = &ASTCtx.Idents.get("MPI_Recv"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Recv); + MPIType.push_back(IdentInfo_MPI_Recv); + assert(IdentInfo_MPI_Recv); + + IdentInfo_MPI_Irecv = &ASTCtx.Idents.get("MPI_Irecv"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Irecv); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Irecv); + MPIType.push_back(IdentInfo_MPI_Irecv); + assert(IdentInfo_MPI_Irecv); +} + +void MPIFunctionClassifier::initCollectiveIdentifiers(ASTContext &ASTCtx) { + // Copy identifiers into the correct classification containers. + IdentInfo_MPI_Scatter = &ASTCtx.Idents.get("MPI_Scatter"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Scatter); + MPIPointToCollTypes.push_back(IdentInfo_MPI_Scatter); + MPIType.push_back(IdentInfo_MPI_Scatter); + assert(IdentInfo_MPI_Scatter); + + IdentInfo_MPI_Iscatter = &ASTCtx.Idents.get("MPI_Iscatter"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Iscatter); + MPIPointToCollTypes.push_back(IdentInfo_MPI_Iscatter); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Iscatter); + MPIType.push_back(IdentInfo_MPI_Iscatter); + assert(IdentInfo_MPI_Iscatter); + + IdentInfo_MPI_Gather = &ASTCtx.Idents.get("MPI_Gather"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Gather); + MPICollToPointTypes.push_back(IdentInfo_MPI_Gather); + MPIType.push_back(IdentInfo_MPI_Gather); + assert(IdentInfo_MPI_Gather); + + IdentInfo_MPI_Igather = &ASTCtx.Idents.get("MPI_Igather"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Igather); + MPICollToPointTypes.push_back(IdentInfo_MPI_Igather); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Igather); + MPIType.push_back(IdentInfo_MPI_Igather); + assert(IdentInfo_MPI_Igather); + + IdentInfo_MPI_Allgather = &ASTCtx.Idents.get("MPI_Allgather"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Allgather); + MPICollToCollTypes.push_back(IdentInfo_MPI_Allgather); + MPIType.push_back(IdentInfo_MPI_Allgather); + assert(IdentInfo_MPI_Allgather); + + IdentInfo_MPI_Iallgather = &ASTCtx.Idents.get("MPI_Iallgather"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Iallgather); + MPICollToCollTypes.push_back(IdentInfo_MPI_Iallgather); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Iallgather); + MPIType.push_back(IdentInfo_MPI_Iallgather); + assert(IdentInfo_MPI_Iallgather); + + IdentInfo_MPI_Bcast = &ASTCtx.Idents.get("MPI_Bcast"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Bcast); + MPIPointToCollTypes.push_back(IdentInfo_MPI_Bcast); + MPIType.push_back(IdentInfo_MPI_Bcast); + assert(IdentInfo_MPI_Bcast); + + IdentInfo_MPI_Ibcast = &ASTCtx.Idents.get("MPI_Ibcast"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Ibcast); + MPIPointToCollTypes.push_back(IdentInfo_MPI_Ibcast); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Ibcast); + MPIType.push_back(IdentInfo_MPI_Ibcast); + assert(IdentInfo_MPI_Ibcast); + + IdentInfo_MPI_Reduce = &ASTCtx.Idents.get("MPI_Reduce"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Reduce); + MPICollToPointTypes.push_back(IdentInfo_MPI_Reduce); + MPIType.push_back(IdentInfo_MPI_Reduce); + assert(IdentInfo_MPI_Reduce); + + IdentInfo_MPI_Ireduce = &ASTCtx.Idents.get("MPI_Ireduce"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Ireduce); + MPICollToPointTypes.push_back(IdentInfo_MPI_Ireduce); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Ireduce); + MPIType.push_back(IdentInfo_MPI_Ireduce); + assert(IdentInfo_MPI_Ireduce); + + IdentInfo_MPI_Allreduce = &ASTCtx.Idents.get("MPI_Allreduce"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Allreduce); + MPICollToCollTypes.push_back(IdentInfo_MPI_Allreduce); + MPIType.push_back(IdentInfo_MPI_Allreduce); + assert(IdentInfo_MPI_Allreduce); + + IdentInfo_MPI_Iallreduce = &ASTCtx.Idents.get("MPI_Iallreduce"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Iallreduce); + MPICollToCollTypes.push_back(IdentInfo_MPI_Iallreduce); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Iallreduce); + MPIType.push_back(IdentInfo_MPI_Iallreduce); + assert(IdentInfo_MPI_Iallreduce); + + IdentInfo_MPI_Alltoall = &ASTCtx.Idents.get("MPI_Alltoall"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Alltoall); + MPICollToCollTypes.push_back(IdentInfo_MPI_Alltoall); + MPIType.push_back(IdentInfo_MPI_Alltoall); + assert(IdentInfo_MPI_Alltoall); + + IdentInfo_MPI_Ialltoall = &ASTCtx.Idents.get("MPI_Ialltoall"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Ialltoall); + MPICollToCollTypes.push_back(IdentInfo_MPI_Ialltoall); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Ialltoall); + MPIType.push_back(IdentInfo_MPI_Ialltoall); + assert(IdentInfo_MPI_Ialltoall); +} + +void MPIFunctionClassifier::initAdditionalIdentifiers(ASTContext &ASTCtx) { + IdentInfo_MPI_Comm_rank = &ASTCtx.Idents.get("MPI_Comm_rank"); + MPIType.push_back(IdentInfo_MPI_Comm_rank); + assert(IdentInfo_MPI_Comm_rank); + + IdentInfo_MPI_Comm_size = &ASTCtx.Idents.get("MPI_Comm_size"); + MPIType.push_back(IdentInfo_MPI_Comm_size); + assert(IdentInfo_MPI_Comm_size); + + IdentInfo_MPI_Wait = &ASTCtx.Idents.get("MPI_Wait"); + MPIType.push_back(IdentInfo_MPI_Wait); + assert(IdentInfo_MPI_Wait); + + IdentInfo_MPI_Waitall = &ASTCtx.Idents.get("MPI_Waitall"); + MPIType.push_back(IdentInfo_MPI_Waitall); + assert(IdentInfo_MPI_Waitall); + + IdentInfo_MPI_Barrier = &ASTCtx.Idents.get("MPI_Barrier"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Barrier); + MPIType.push_back(IdentInfo_MPI_Barrier); + assert(IdentInfo_MPI_Barrier); +} + +// general identifiers +bool MPIFunctionClassifier::isMPIType(const IdentifierInfo *IdentInfo) const { + return llvm::is_contained(MPIType, IdentInfo); +} + +bool MPIFunctionClassifier::isNonBlockingType( + const IdentifierInfo *IdentInfo) const { + return llvm::is_contained(MPINonBlockingTypes, IdentInfo); +} + +// point-to-point identifiers +bool MPIFunctionClassifier::isPointToPointType( + const IdentifierInfo *IdentInfo) const { + return llvm::is_contained(MPIPointToPointTypes, IdentInfo); +} + +// collective identifiers +bool MPIFunctionClassifier::isCollectiveType( + const IdentifierInfo *IdentInfo) const { + return llvm::is_contained(MPICollectiveTypes, IdentInfo); +} + +bool MPIFunctionClassifier::isCollToColl( + const IdentifierInfo *IdentInfo) const { + return llvm::is_contained(MPICollToCollTypes, IdentInfo); +} + +bool MPIFunctionClassifier::isScatterType( + const IdentifierInfo *IdentInfo) const { + return IdentInfo == IdentInfo_MPI_Scatter || + IdentInfo == IdentInfo_MPI_Iscatter; +} + +bool MPIFunctionClassifier::isGatherType( + const IdentifierInfo *IdentInfo) const { + return IdentInfo == IdentInfo_MPI_Gather || + IdentInfo == IdentInfo_MPI_Igather || + IdentInfo == IdentInfo_MPI_Allgather || + IdentInfo == IdentInfo_MPI_Iallgather; +} + +bool MPIFunctionClassifier::isAllgatherType( + const IdentifierInfo *IdentInfo) const { + return IdentInfo == IdentInfo_MPI_Allgather || + IdentInfo == IdentInfo_MPI_Iallgather; +} + +bool MPIFunctionClassifier::isAlltoallType( + const IdentifierInfo *IdentInfo) const { + return IdentInfo == IdentInfo_MPI_Alltoall || + IdentInfo == IdentInfo_MPI_Ialltoall; +} + +bool MPIFunctionClassifier::isBcastType(const IdentifierInfo *IdentInfo) const { + return IdentInfo == IdentInfo_MPI_Bcast || IdentInfo == IdentInfo_MPI_Ibcast; +} + +bool MPIFunctionClassifier::isReduceType( + const IdentifierInfo *IdentInfo) const { + return IdentInfo == IdentInfo_MPI_Reduce || + IdentInfo == IdentInfo_MPI_Ireduce || + IdentInfo == IdentInfo_MPI_Allreduce || + IdentInfo == IdentInfo_MPI_Iallreduce; +} + +// additional identifiers +bool MPIFunctionClassifier::isMPI_Wait(const IdentifierInfo *IdentInfo) const { + return IdentInfo == IdentInfo_MPI_Wait; +} + +bool MPIFunctionClassifier::isMPI_Waitall( + const IdentifierInfo *IdentInfo) const { + return IdentInfo == IdentInfo_MPI_Waitall; +} + +bool MPIFunctionClassifier::isWaitType(const IdentifierInfo *IdentInfo) const { + return IdentInfo == IdentInfo_MPI_Wait || IdentInfo == IdentInfo_MPI_Waitall; +} + +} // end of namespace: mpi +} // end of namespace: ento +} // end of namespace: clang diff --git a/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.h b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.h new file mode 100644 index 000000000000..8eeb1b48f604 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.h @@ -0,0 +1,97 @@ +//===-- MPIFunctionClassifier.h - classifies MPI functions ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines functionality to identify and classify MPI functions. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPIFUNCTIONCLASSIFIER_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPIFUNCTIONCLASSIFIER_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +namespace clang { +namespace ento { +namespace mpi { + +class MPIFunctionClassifier { +public: + MPIFunctionClassifier(ASTContext &ASTCtx) { identifierInit(ASTCtx); } + + // general identifiers + bool isMPIType(const IdentifierInfo *const IdentInfo) const; + bool isNonBlockingType(const IdentifierInfo *const IdentInfo) const; + + // point-to-point identifiers + bool isPointToPointType(const IdentifierInfo *const IdentInfo) const; + + // collective identifiers + bool isCollectiveType(const IdentifierInfo *const IdentInfo) const; + bool isCollToColl(const IdentifierInfo *const IdentInfo) const; + bool isScatterType(const IdentifierInfo *const IdentInfo) const; + bool isGatherType(const IdentifierInfo *const IdentInfo) const; + bool isAllgatherType(const IdentifierInfo *const IdentInfo) const; + bool isAlltoallType(const IdentifierInfo *const IdentInfo) const; + bool isReduceType(const IdentifierInfo *const IdentInfo) const; + bool isBcastType(const IdentifierInfo *const IdentInfo) const; + + // additional identifiers + bool isMPI_Wait(const IdentifierInfo *const IdentInfo) const; + bool isMPI_Waitall(const IdentifierInfo *const IdentInfo) const; + bool isWaitType(const IdentifierInfo *const IdentInfo) const; + +private: + // Initializes function identifiers, to recognize them during analysis. + void identifierInit(ASTContext &ASTCtx); + void initPointToPointIdentifiers(ASTContext &ASTCtx); + void initCollectiveIdentifiers(ASTContext &ASTCtx); + void initAdditionalIdentifiers(ASTContext &ASTCtx); + + // The containers are used, to enable classification of MPI-functions during + // analysis. + llvm::SmallVector MPINonBlockingTypes; + + llvm::SmallVector MPIPointToPointTypes; + llvm::SmallVector MPICollectiveTypes; + + llvm::SmallVector MPIPointToCollTypes; + llvm::SmallVector MPICollToPointTypes; + llvm::SmallVector MPICollToCollTypes; + + llvm::SmallVector MPIType; + + // point-to-point functions + IdentifierInfo *IdentInfo_MPI_Send{nullptr}, *IdentInfo_MPI_Isend{nullptr}, + *IdentInfo_MPI_Ssend{nullptr}, *IdentInfo_MPI_Issend{nullptr}, + *IdentInfo_MPI_Bsend{nullptr}, *IdentInfo_MPI_Ibsend{nullptr}, + *IdentInfo_MPI_Rsend{nullptr}, *IdentInfo_MPI_Irsend{nullptr}, + *IdentInfo_MPI_Recv{nullptr}, *IdentInfo_MPI_Irecv{nullptr}; + + // collective functions + IdentifierInfo *IdentInfo_MPI_Scatter{nullptr}, + *IdentInfo_MPI_Iscatter{nullptr}, *IdentInfo_MPI_Gather{nullptr}, + *IdentInfo_MPI_Igather{nullptr}, *IdentInfo_MPI_Allgather{nullptr}, + *IdentInfo_MPI_Iallgather{nullptr}, *IdentInfo_MPI_Bcast{nullptr}, + *IdentInfo_MPI_Ibcast{nullptr}, *IdentInfo_MPI_Reduce{nullptr}, + *IdentInfo_MPI_Ireduce{nullptr}, *IdentInfo_MPI_Allreduce{nullptr}, + *IdentInfo_MPI_Iallreduce{nullptr}, *IdentInfo_MPI_Alltoall{nullptr}, + *IdentInfo_MPI_Ialltoall{nullptr}, *IdentInfo_MPI_Barrier{nullptr}; + + // additional functions + IdentifierInfo *IdentInfo_MPI_Comm_rank{nullptr}, + *IdentInfo_MPI_Comm_size{nullptr}, *IdentInfo_MPI_Wait{nullptr}, + *IdentInfo_MPI_Waitall{nullptr}; +}; + +} // end of namespace: mpi +} // end of namespace: ento +} // end of namespace: clang + +#endif diff --git a/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h new file mode 100644 index 000000000000..27ec950d31eb --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h @@ -0,0 +1,68 @@ +//===-- MPITypes.h - Functionality to model MPI concepts --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file provides definitions to model concepts of MPI. The mpi::Request +/// class defines a wrapper class, in order to make MPI requests trackable for +/// path-sensitive analysis. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPITYPES_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPITYPES_H + +#include "MPIFunctionClassifier.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "llvm/ADT/SmallSet.h" + +namespace clang { +namespace ento { +namespace mpi { + +class Request { +public: + enum State : unsigned char { Nonblocking, Wait }; + + Request(State S) : CurrentState{S} {} + + void Profile(llvm::FoldingSetNodeID &Id) const { + Id.AddInteger(CurrentState); + } + + bool operator==(const Request &ToCompare) const { + return CurrentState == ToCompare.CurrentState; + } + + const State CurrentState; +}; + +// The RequestMap stores MPI requests which are identified by their memory +// region. Requests are used in MPI to complete nonblocking operations with wait +// operations. A custom map implementation is used, in order to make it +// available in an arbitrary amount of translation units. +struct RequestMap {}; +typedef llvm::ImmutableMap + RequestMapImpl; + +} // end of namespace: mpi + + +template <> +struct ProgramStateTrait + : public ProgramStatePartialTrait { + static void *GDMIndex() { + static int index = 0; + return &index; + } +}; + +} // end of namespace: ento +} // end of namespace: clang +#endif diff --git a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp index 083f6c01bc2f..b7b6f42b2910 100644 --- a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -1,1503 +1,1562 @@ //== MemRegion.cpp - Abstract memory regions for static analysis --*- C++ -*--// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines MemRegion and its subclasses. MemRegion defines a // partially-typed abstraction of memory useful for path-sensitive dataflow // analyses. // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/AST/Attr.h" #include "clang/AST/CharUnits.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/RecordLayout.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Analysis/Support/BumpVector.h" #include "clang/Basic/SourceManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; //===----------------------------------------------------------------------===// // MemRegion Construction. //===----------------------------------------------------------------------===// template struct MemRegionManagerTrait; template RegionTy* MemRegionManager::getRegion(const A1 a1) { const typename MemRegionManagerTrait::SuperRegionTy *superRegion = MemRegionManagerTrait::getSuperRegion(*this, a1); llvm::FoldingSetNodeID ID; RegionTy::ProfileRegion(ID, a1, superRegion); void *InsertPos; RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, InsertPos)); if (!R) { R = A.Allocate(); new (R) RegionTy(a1, superRegion); Regions.InsertNode(R, InsertPos); } return R; } template RegionTy* MemRegionManager::getSubRegion(const A1 a1, const MemRegion *superRegion) { llvm::FoldingSetNodeID ID; RegionTy::ProfileRegion(ID, a1, superRegion); void *InsertPos; RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, InsertPos)); if (!R) { R = A.Allocate(); new (R) RegionTy(a1, superRegion); Regions.InsertNode(R, InsertPos); } return R; } template RegionTy* MemRegionManager::getRegion(const A1 a1, const A2 a2) { const typename MemRegionManagerTrait::SuperRegionTy *superRegion = MemRegionManagerTrait::getSuperRegion(*this, a1, a2); llvm::FoldingSetNodeID ID; RegionTy::ProfileRegion(ID, a1, a2, superRegion); void *InsertPos; RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, InsertPos)); if (!R) { R = A.Allocate(); new (R) RegionTy(a1, a2, superRegion); Regions.InsertNode(R, InsertPos); } return R; } template RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, const MemRegion *superRegion) { llvm::FoldingSetNodeID ID; RegionTy::ProfileRegion(ID, a1, a2, superRegion); void *InsertPos; RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, InsertPos)); if (!R) { R = A.Allocate(); new (R) RegionTy(a1, a2, superRegion); Regions.InsertNode(R, InsertPos); } return R; } template RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, const A3 a3, const MemRegion *superRegion) { llvm::FoldingSetNodeID ID; RegionTy::ProfileRegion(ID, a1, a2, a3, superRegion); void *InsertPos; RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, InsertPos)); if (!R) { R = A.Allocate(); new (R) RegionTy(a1, a2, a3, superRegion); Regions.InsertNode(R, InsertPos); } return R; } //===----------------------------------------------------------------------===// // Object destruction. //===----------------------------------------------------------------------===// MemRegion::~MemRegion() {} MemRegionManager::~MemRegionManager() { // All regions and their data are BumpPtrAllocated. No need to call // their destructors. } //===----------------------------------------------------------------------===// // Basic methods. //===----------------------------------------------------------------------===// bool SubRegion::isSubRegionOf(const MemRegion* R) const { const MemRegion* r = getSuperRegion(); while (r != nullptr) { if (r == R) return true; if (const SubRegion* sr = dyn_cast(r)) r = sr->getSuperRegion(); else break; } return false; } MemRegionManager* SubRegion::getMemRegionManager() const { const SubRegion* r = this; do { const MemRegion *superRegion = r->getSuperRegion(); if (const SubRegion *sr = dyn_cast(superRegion)) { r = sr; continue; } return superRegion->getMemRegionManager(); } while (1); } const StackFrameContext *VarRegion::getStackFrame() const { const StackSpaceRegion *SSR = dyn_cast(getMemorySpace()); return SSR ? SSR->getStackFrame() : nullptr; } //===----------------------------------------------------------------------===// // Region extents. //===----------------------------------------------------------------------===// DefinedOrUnknownSVal TypedValueRegion::getExtent(SValBuilder &svalBuilder) const { ASTContext &Ctx = svalBuilder.getContext(); QualType T = getDesugaredValueType(Ctx); if (isa(T)) return nonloc::SymbolVal(svalBuilder.getSymbolManager().getExtentSymbol(this)); if (T->isIncompleteType()) return UnknownVal(); CharUnits size = Ctx.getTypeSizeInChars(T); QualType sizeTy = svalBuilder.getArrayIndexType(); return svalBuilder.makeIntVal(size.getQuantity(), sizeTy); } DefinedOrUnknownSVal FieldRegion::getExtent(SValBuilder &svalBuilder) const { // Force callers to deal with bitfields explicitly. if (getDecl()->isBitField()) return UnknownVal(); DefinedOrUnknownSVal Extent = DeclRegion::getExtent(svalBuilder); // A zero-length array at the end of a struct often stands for dynamically- // allocated extra memory. if (Extent.isZeroConstant()) { QualType T = getDesugaredValueType(svalBuilder.getContext()); if (isa(T)) return UnknownVal(); } return Extent; } DefinedOrUnknownSVal AllocaRegion::getExtent(SValBuilder &svalBuilder) const { return nonloc::SymbolVal(svalBuilder.getSymbolManager().getExtentSymbol(this)); } DefinedOrUnknownSVal SymbolicRegion::getExtent(SValBuilder &svalBuilder) const { return nonloc::SymbolVal(svalBuilder.getSymbolManager().getExtentSymbol(this)); } DefinedOrUnknownSVal StringRegion::getExtent(SValBuilder &svalBuilder) const { return svalBuilder.makeIntVal(getStringLiteral()->getByteLength()+1, svalBuilder.getArrayIndexType()); } ObjCIvarRegion::ObjCIvarRegion(const ObjCIvarDecl *ivd, const MemRegion* sReg) : DeclRegion(ivd, sReg, ObjCIvarRegionKind) {} const ObjCIvarDecl *ObjCIvarRegion::getDecl() const { return cast(D); } QualType ObjCIvarRegion::getValueType() const { return getDecl()->getType(); } QualType CXXBaseObjectRegion::getValueType() const { return QualType(getDecl()->getTypeForDecl(), 0); } //===----------------------------------------------------------------------===// // FoldingSet profiling. //===----------------------------------------------------------------------===// void MemSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(static_cast(getKind())); } void StackSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(static_cast(getKind())); ID.AddPointer(getStackFrame()); } void StaticGlobalSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(static_cast(getKind())); ID.AddPointer(getCodeRegion()); } void StringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const StringLiteral* Str, const MemRegion* superRegion) { ID.AddInteger(static_cast(StringRegionKind)); ID.AddPointer(Str); ID.AddPointer(superRegion); } void ObjCStringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const ObjCStringLiteral* Str, const MemRegion* superRegion) { ID.AddInteger(static_cast(ObjCStringRegionKind)); ID.AddPointer(Str); ID.AddPointer(superRegion); } void AllocaRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Expr *Ex, unsigned cnt, const MemRegion *superRegion) { ID.AddInteger(static_cast(AllocaRegionKind)); ID.AddPointer(Ex); ID.AddInteger(cnt); ID.AddPointer(superRegion); } void AllocaRegion::Profile(llvm::FoldingSetNodeID& ID) const { ProfileRegion(ID, Ex, Cnt, superRegion); } void CompoundLiteralRegion::Profile(llvm::FoldingSetNodeID& ID) const { CompoundLiteralRegion::ProfileRegion(ID, CL, superRegion); } void CompoundLiteralRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const CompoundLiteralExpr *CL, const MemRegion* superRegion) { ID.AddInteger(static_cast(CompoundLiteralRegionKind)); ID.AddPointer(CL); ID.AddPointer(superRegion); } void CXXThisRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, const PointerType *PT, const MemRegion *sRegion) { ID.AddInteger(static_cast(CXXThisRegionKind)); ID.AddPointer(PT); ID.AddPointer(sRegion); } void CXXThisRegion::Profile(llvm::FoldingSetNodeID &ID) const { CXXThisRegion::ProfileRegion(ID, ThisPointerTy, superRegion); } void ObjCIvarRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const ObjCIvarDecl *ivd, const MemRegion* superRegion) { DeclRegion::ProfileRegion(ID, ivd, superRegion, ObjCIvarRegionKind); } void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl *D, const MemRegion* superRegion, Kind k) { ID.AddInteger(static_cast(k)); ID.AddPointer(D); ID.AddPointer(superRegion); } void DeclRegion::Profile(llvm::FoldingSetNodeID& ID) const { DeclRegion::ProfileRegion(ID, D, superRegion, getKind()); } void VarRegion::Profile(llvm::FoldingSetNodeID &ID) const { VarRegion::ProfileRegion(ID, getDecl(), superRegion); } void SymbolicRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, SymbolRef sym, const MemRegion *sreg) { ID.AddInteger(static_cast(MemRegion::SymbolicRegionKind)); ID.Add(sym); ID.AddPointer(sreg); } void SymbolicRegion::Profile(llvm::FoldingSetNodeID& ID) const { SymbolicRegion::ProfileRegion(ID, sym, getSuperRegion()); } void ElementRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, QualType ElementType, SVal Idx, const MemRegion* superRegion) { ID.AddInteger(MemRegion::ElementRegionKind); ID.Add(ElementType); ID.AddPointer(superRegion); Idx.Profile(ID); } void ElementRegion::Profile(llvm::FoldingSetNodeID& ID) const { ElementRegion::ProfileRegion(ID, ElementType, Index, superRegion); } void FunctionCodeRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const NamedDecl *FD, const MemRegion*) { ID.AddInteger(MemRegion::FunctionCodeRegionKind); ID.AddPointer(FD); } void FunctionCodeRegion::Profile(llvm::FoldingSetNodeID& ID) const { FunctionCodeRegion::ProfileRegion(ID, FD, superRegion); } void BlockCodeRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const BlockDecl *BD, CanQualType, const AnalysisDeclContext *AC, const MemRegion*) { ID.AddInteger(MemRegion::BlockCodeRegionKind); ID.AddPointer(BD); } void BlockCodeRegion::Profile(llvm::FoldingSetNodeID& ID) const { BlockCodeRegion::ProfileRegion(ID, BD, locTy, AC, superRegion); } void BlockDataRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const BlockCodeRegion *BC, const LocationContext *LC, unsigned BlkCount, const MemRegion *sReg) { ID.AddInteger(MemRegion::BlockDataRegionKind); ID.AddPointer(BC); ID.AddPointer(LC); ID.AddInteger(BlkCount); ID.AddPointer(sReg); } void BlockDataRegion::Profile(llvm::FoldingSetNodeID& ID) const { BlockDataRegion::ProfileRegion(ID, BC, LC, BlockCount, getSuperRegion()); } void CXXTempObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, Expr const *Ex, const MemRegion *sReg) { ID.AddPointer(Ex); ID.AddPointer(sReg); } void CXXTempObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { ProfileRegion(ID, Ex, getSuperRegion()); } void CXXBaseObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, const CXXRecordDecl *RD, bool IsVirtual, const MemRegion *SReg) { ID.AddPointer(RD); ID.AddBoolean(IsVirtual); ID.AddPointer(SReg); } void CXXBaseObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { ProfileRegion(ID, getDecl(), isVirtual(), superRegion); } //===----------------------------------------------------------------------===// // Region anchors. //===----------------------------------------------------------------------===// void GlobalsSpaceRegion::anchor() { } void HeapSpaceRegion::anchor() { } void UnknownSpaceRegion::anchor() { } void StackLocalsSpaceRegion::anchor() { } void StackArgumentsSpaceRegion::anchor() { } void TypedRegion::anchor() { } void TypedValueRegion::anchor() { } void CodeTextRegion::anchor() { } void SubRegion::anchor() { } //===----------------------------------------------------------------------===// // Region pretty-printing. //===----------------------------------------------------------------------===// LLVM_DUMP_METHOD void MemRegion::dump() const { dumpToStream(llvm::errs()); } std::string MemRegion::getString() const { std::string s; llvm::raw_string_ostream os(s); dumpToStream(os); return os.str(); } void MemRegion::dumpToStream(raw_ostream &os) const { os << ""; } void AllocaRegion::dumpToStream(raw_ostream &os) const { os << "alloca{" << static_cast(Ex) << ',' << Cnt << '}'; } void FunctionCodeRegion::dumpToStream(raw_ostream &os) const { os << "code{" << getDecl()->getDeclName().getAsString() << '}'; } void BlockCodeRegion::dumpToStream(raw_ostream &os) const { os << "block_code{" << static_cast(this) << '}'; } void BlockDataRegion::dumpToStream(raw_ostream &os) const { os << "block_data{" << BC; os << "; "; for (BlockDataRegion::referenced_vars_iterator I = referenced_vars_begin(), E = referenced_vars_end(); I != E; ++I) os << "(" << I.getCapturedRegion() << "," << I.getOriginalRegion() << ") "; os << '}'; } void CompoundLiteralRegion::dumpToStream(raw_ostream &os) const { // FIXME: More elaborate pretty-printing. os << "{ " << static_cast(CL) << " }"; } void CXXTempObjectRegion::dumpToStream(raw_ostream &os) const { os << "temp_object{" << getValueType().getAsString() << ',' << static_cast(Ex) << '}'; } void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const { os << "base{" << superRegion << ',' << getDecl()->getName() << '}'; } void CXXThisRegion::dumpToStream(raw_ostream &os) const { os << "this"; } void ElementRegion::dumpToStream(raw_ostream &os) const { os << "element{" << superRegion << ',' << Index << ',' << getElementType().getAsString() << '}'; } void FieldRegion::dumpToStream(raw_ostream &os) const { os << superRegion << "->" << *getDecl(); } void ObjCIvarRegion::dumpToStream(raw_ostream &os) const { os << "ivar{" << superRegion << ',' << *getDecl() << '}'; } void StringRegion::dumpToStream(raw_ostream &os) const { assert(Str != nullptr && "Expecting non-null StringLiteral"); Str->printPretty(os, nullptr, PrintingPolicy(getContext().getLangOpts())); } void ObjCStringRegion::dumpToStream(raw_ostream &os) const { assert(Str != nullptr && "Expecting non-null ObjCStringLiteral"); Str->printPretty(os, nullptr, PrintingPolicy(getContext().getLangOpts())); } void SymbolicRegion::dumpToStream(raw_ostream &os) const { os << "SymRegion{" << sym << '}'; } void VarRegion::dumpToStream(raw_ostream &os) const { os << *cast(D); } LLVM_DUMP_METHOD void RegionRawOffset::dump() const { dumpToStream(llvm::errs()); } void RegionRawOffset::dumpToStream(raw_ostream &os) const { os << "raw_offset{" << getRegion() << ',' << getOffset().getQuantity() << '}'; } void CodeSpaceRegion::dumpToStream(raw_ostream &os) const { os << "CodeSpaceRegion"; } void StaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const { os << "StaticGlobalsMemSpace{" << CR << '}'; } void GlobalInternalSpaceRegion::dumpToStream(raw_ostream &os) const { os << "GlobalInternalSpaceRegion"; } void GlobalSystemSpaceRegion::dumpToStream(raw_ostream &os) const { os << "GlobalSystemSpaceRegion"; } void GlobalImmutableSpaceRegion::dumpToStream(raw_ostream &os) const { os << "GlobalImmutableSpaceRegion"; } void HeapSpaceRegion::dumpToStream(raw_ostream &os) const { os << "HeapSpaceRegion"; } void UnknownSpaceRegion::dumpToStream(raw_ostream &os) const { os << "UnknownSpaceRegion"; } void StackArgumentsSpaceRegion::dumpToStream(raw_ostream &os) const { os << "StackArgumentsSpaceRegion"; } void StackLocalsSpaceRegion::dumpToStream(raw_ostream &os) const { os << "StackLocalsSpaceRegion"; } bool MemRegion::canPrintPretty() const { return canPrintPrettyAsExpr(); } bool MemRegion::canPrintPrettyAsExpr() const { return false; } void MemRegion::printPretty(raw_ostream &os) const { assert(canPrintPretty() && "This region cannot be printed pretty."); os << "'"; printPrettyAsExpr(os); os << "'"; } void MemRegion::printPrettyAsExpr(raw_ostream &os) const { llvm_unreachable("This region cannot be printed pretty."); } bool VarRegion::canPrintPrettyAsExpr() const { return true; } void VarRegion::printPrettyAsExpr(raw_ostream &os) const { os << getDecl()->getName(); } bool ObjCIvarRegion::canPrintPrettyAsExpr() const { return true; } void ObjCIvarRegion::printPrettyAsExpr(raw_ostream &os) const { os << getDecl()->getName(); } bool FieldRegion::canPrintPretty() const { return true; } bool FieldRegion::canPrintPrettyAsExpr() const { return superRegion->canPrintPrettyAsExpr(); } void FieldRegion::printPrettyAsExpr(raw_ostream &os) const { assert(canPrintPrettyAsExpr()); superRegion->printPrettyAsExpr(os); os << "." << getDecl()->getName(); } void FieldRegion::printPretty(raw_ostream &os) const { if (canPrintPrettyAsExpr()) { os << "\'"; printPrettyAsExpr(os); os << "'"; } else { os << "field " << "\'" << getDecl()->getName() << "'"; } } bool CXXBaseObjectRegion::canPrintPrettyAsExpr() const { return superRegion->canPrintPrettyAsExpr(); } void CXXBaseObjectRegion::printPrettyAsExpr(raw_ostream &os) const { superRegion->printPrettyAsExpr(os); } +std::string MemRegion::getDescriptiveName(bool UseQuotes) const { + std::string VariableName; + std::string ArrayIndices; + const MemRegion *R = this; + SmallString<50> buf; + llvm::raw_svector_ostream os(buf); + + // Obtain array indices to add them to the variable name. + const ElementRegion *ER = nullptr; + while ((ER = R->getAs())) { + // Index is a ConcreteInt. + if (auto CI = ER->getIndex().getAs()) { + llvm::SmallString<2> Idx; + CI->getValue().toString(Idx); + ArrayIndices = (llvm::Twine("[") + Idx.str() + "]" + ArrayIndices).str(); + } + // If not a ConcreteInt, try to obtain the variable + // name by calling 'getDescriptiveName' recursively. + else { + std::string Idx = ER->getDescriptiveName(false); + if (!Idx.empty()) { + ArrayIndices = (llvm::Twine("[") + Idx + "]" + ArrayIndices).str(); + } + } + R = ER->getSuperRegion(); + } + + // Get variable name. + if (R && R->canPrintPrettyAsExpr()) { + R->printPrettyAsExpr(os); + if (UseQuotes) { + return (llvm::Twine("'") + os.str() + ArrayIndices + "'").str(); + } else { + return (llvm::Twine(os.str()) + ArrayIndices).str(); + } + } + + return VariableName; +} + +SourceRange MemRegion::sourceRange() const { + const VarRegion *const VR = dyn_cast(this->getBaseRegion()); + const FieldRegion *const FR = dyn_cast(this); + + // Check for more specific regions first. + // FieldRegion + if (FR) { + return FR->getDecl()->getSourceRange(); + } + // VarRegion + else if (VR) { + return VR->getDecl()->getSourceRange(); + } + // Return invalid source range (can be checked by client). + else { + return SourceRange{}; + } +} + //===----------------------------------------------------------------------===// // MemRegionManager methods. //===----------------------------------------------------------------------===// template const REG *MemRegionManager::LazyAllocate(REG*& region) { if (!region) { region = A.Allocate(); new (region) REG(this); } return region; } template const REG *MemRegionManager::LazyAllocate(REG*& region, ARG a) { if (!region) { region = A.Allocate(); new (region) REG(this, a); } return region; } const StackLocalsSpaceRegion* MemRegionManager::getStackLocalsRegion(const StackFrameContext *STC) { assert(STC); StackLocalsSpaceRegion *&R = StackLocalsSpaceRegions[STC]; if (R) return R; R = A.Allocate(); new (R) StackLocalsSpaceRegion(this, STC); return R; } const StackArgumentsSpaceRegion * MemRegionManager::getStackArgumentsRegion(const StackFrameContext *STC) { assert(STC); StackArgumentsSpaceRegion *&R = StackArgumentsSpaceRegions[STC]; if (R) return R; R = A.Allocate(); new (R) StackArgumentsSpaceRegion(this, STC); return R; } const GlobalsSpaceRegion *MemRegionManager::getGlobalsRegion(MemRegion::Kind K, const CodeTextRegion *CR) { if (!CR) { if (K == MemRegion::GlobalSystemSpaceRegionKind) return LazyAllocate(SystemGlobals); if (K == MemRegion::GlobalImmutableSpaceRegionKind) return LazyAllocate(ImmutableGlobals); assert(K == MemRegion::GlobalInternalSpaceRegionKind); return LazyAllocate(InternalGlobals); } assert(K == MemRegion::StaticGlobalSpaceRegionKind); StaticGlobalSpaceRegion *&R = StaticsGlobalSpaceRegions[CR]; if (R) return R; R = A.Allocate(); new (R) StaticGlobalSpaceRegion(this, CR); return R; } const HeapSpaceRegion *MemRegionManager::getHeapRegion() { return LazyAllocate(heap); } const UnknownSpaceRegion *MemRegionManager::getUnknownRegion() { return LazyAllocate(unknown); } const CodeSpaceRegion *MemRegionManager::getCodeRegion() { return LazyAllocate(code); } //===----------------------------------------------------------------------===// // Constructing regions. //===----------------------------------------------------------------------===// const StringRegion* MemRegionManager::getStringRegion(const StringLiteral* Str){ return getSubRegion(Str, getGlobalsRegion()); } const ObjCStringRegion * MemRegionManager::getObjCStringRegion(const ObjCStringLiteral* Str){ return getSubRegion(Str, getGlobalsRegion()); } /// Look through a chain of LocationContexts to either find the /// StackFrameContext that matches a DeclContext, or find a VarRegion /// for a variable captured by a block. static llvm::PointerUnion getStackOrCaptureRegionForDeclContext(const LocationContext *LC, const DeclContext *DC, const VarDecl *VD) { while (LC) { if (const StackFrameContext *SFC = dyn_cast(LC)) { if (cast(SFC->getDecl()) == DC) return SFC; } if (const BlockInvocationContext *BC = dyn_cast(LC)) { const BlockDataRegion *BR = static_cast(BC->getContextData()); // FIXME: This can be made more efficient. for (BlockDataRegion::referenced_vars_iterator I = BR->referenced_vars_begin(), E = BR->referenced_vars_end(); I != E; ++I) { if (const VarRegion *VR = dyn_cast(I.getOriginalRegion())) if (VR->getDecl() == VD) return cast(I.getCapturedRegion()); } } LC = LC->getParent(); } return (const StackFrameContext *)nullptr; } const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, const LocationContext *LC) { const MemRegion *sReg = nullptr; if (D->hasGlobalStorage() && !D->isStaticLocal()) { // First handle the globals defined in system headers. if (C.getSourceManager().isInSystemHeader(D->getLocation())) { // Whitelist the system globals which often DO GET modified, assume the // rest are immutable. if (D->getName().find("errno") != StringRef::npos) sReg = getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind); else sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind); // Treat other globals as GlobalInternal unless they are constants. } else { QualType GQT = D->getType(); const Type *GT = GQT.getTypePtrOrNull(); // TODO: We could walk the complex types here and see if everything is // constified. if (GT && GQT.isConstQualified() && GT->isArithmeticType()) sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind); else sReg = getGlobalsRegion(); } // Finally handle static locals. } else { // FIXME: Once we implement scope handling, we will need to properly lookup // 'D' to the proper LocationContext. const DeclContext *DC = D->getDeclContext(); llvm::PointerUnion V = getStackOrCaptureRegionForDeclContext(LC, DC, D); if (V.is()) return V.get(); const StackFrameContext *STC = V.get(); if (!STC) sReg = getUnknownRegion(); else { if (D->hasLocalStorage()) { sReg = isa(D) || isa(D) ? static_cast(getStackArgumentsRegion(STC)) : static_cast(getStackLocalsRegion(STC)); } else { assert(D->isStaticLocal()); const Decl *STCD = STC->getDecl(); if (isa(STCD) || isa(STCD)) sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind, getFunctionCodeRegion(cast(STCD))); else if (const BlockDecl *BD = dyn_cast(STCD)) { // FIXME: The fallback type here is totally bogus -- though it should // never be queried, it will prevent uniquing with the real // BlockCodeRegion. Ideally we'd fix the AST so that we always had a // signature. QualType T; if (const TypeSourceInfo *TSI = BD->getSignatureAsWritten()) T = TSI->getType(); if (T.isNull()) T = getContext().VoidTy; if (!T->getAs()) T = getContext().getFunctionNoProtoType(T); T = getContext().getBlockPointerType(T); const BlockCodeRegion *BTR = getBlockCodeRegion(BD, C.getCanonicalType(T), STC->getAnalysisDeclContext()); sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind, BTR); } else { sReg = getGlobalsRegion(); } } } } return getSubRegion(D, sReg); } const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D, const MemRegion *superR) { return getSubRegion(D, superR); } const BlockDataRegion * MemRegionManager::getBlockDataRegion(const BlockCodeRegion *BC, const LocationContext *LC, unsigned blockCount) { const MemRegion *sReg = nullptr; const BlockDecl *BD = BC->getDecl(); if (!BD->hasCaptures()) { // This handles 'static' blocks. sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind); } else { if (LC) { // FIXME: Once we implement scope handling, we want the parent region // to be the scope. const StackFrameContext *STC = LC->getCurrentStackFrame(); assert(STC); sReg = getStackLocalsRegion(STC); } else { // We allow 'LC' to be NULL for cases where want BlockDataRegions // without context-sensitivity. sReg = getUnknownRegion(); } } return getSubRegion(BC, LC, blockCount, sReg); } const CXXTempObjectRegion * MemRegionManager::getCXXStaticTempObjectRegion(const Expr *Ex) { return getSubRegion( Ex, getGlobalsRegion(MemRegion::GlobalInternalSpaceRegionKind, nullptr)); } const CompoundLiteralRegion* MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr *CL, const LocationContext *LC) { const MemRegion *sReg = nullptr; if (CL->isFileScope()) sReg = getGlobalsRegion(); else { const StackFrameContext *STC = LC->getCurrentStackFrame(); assert(STC); sReg = getStackLocalsRegion(STC); } return getSubRegion(CL, sReg); } const ElementRegion* MemRegionManager::getElementRegion(QualType elementType, NonLoc Idx, const MemRegion* superRegion, ASTContext &Ctx){ QualType T = Ctx.getCanonicalType(elementType).getUnqualifiedType(); llvm::FoldingSetNodeID ID; ElementRegion::ProfileRegion(ID, T, Idx, superRegion); void *InsertPos; MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos); ElementRegion* R = cast_or_null(data); if (!R) { R = A.Allocate(); new (R) ElementRegion(T, Idx, superRegion); Regions.InsertNode(R, InsertPos); } return R; } const FunctionCodeRegion * MemRegionManager::getFunctionCodeRegion(const NamedDecl *FD) { return getSubRegion(FD, getCodeRegion()); } const BlockCodeRegion * MemRegionManager::getBlockCodeRegion(const BlockDecl *BD, CanQualType locTy, AnalysisDeclContext *AC) { return getSubRegion(BD, locTy, AC, getCodeRegion()); } /// getSymbolicRegion - Retrieve or create a "symbolic" memory region. const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) { return getSubRegion(sym, getUnknownRegion()); } const SymbolicRegion *MemRegionManager::getSymbolicHeapRegion(SymbolRef Sym) { return getSubRegion(Sym, getHeapRegion()); } const FieldRegion* MemRegionManager::getFieldRegion(const FieldDecl *d, const MemRegion* superRegion){ return getSubRegion(d, superRegion); } const ObjCIvarRegion* MemRegionManager::getObjCIvarRegion(const ObjCIvarDecl *d, const MemRegion* superRegion) { return getSubRegion(d, superRegion); } const CXXTempObjectRegion* MemRegionManager::getCXXTempObjectRegion(Expr const *E, LocationContext const *LC) { const StackFrameContext *SFC = LC->getCurrentStackFrame(); assert(SFC); return getSubRegion(E, getStackLocalsRegion(SFC)); } /// Checks whether \p BaseClass is a valid virtual or direct non-virtual base /// class of the type of \p Super. static bool isValidBaseClass(const CXXRecordDecl *BaseClass, const TypedValueRegion *Super, bool IsVirtual) { BaseClass = BaseClass->getCanonicalDecl(); const CXXRecordDecl *Class = Super->getValueType()->getAsCXXRecordDecl(); if (!Class) return true; if (IsVirtual) return Class->isVirtuallyDerivedFrom(BaseClass); for (const auto &I : Class->bases()) { if (I.getType()->getAsCXXRecordDecl()->getCanonicalDecl() == BaseClass) return true; } return false; } const CXXBaseObjectRegion * MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD, const MemRegion *Super, bool IsVirtual) { if (isa(Super)) { assert(isValidBaseClass(RD, dyn_cast(Super), IsVirtual)); (void)&isValidBaseClass; if (IsVirtual) { // Virtual base regions should not be layered, since the layout rules // are different. while (const CXXBaseObjectRegion *Base = dyn_cast(Super)) { Super = Base->getSuperRegion(); } assert(Super && !isa(Super)); } } return getSubRegion(RD, IsVirtual, Super); } const CXXThisRegion* MemRegionManager::getCXXThisRegion(QualType thisPointerTy, const LocationContext *LC) { const PointerType *PT = thisPointerTy->getAs(); assert(PT); // Inside the body of the operator() of a lambda a this expr might refer to an // object in one of the parent location contexts. const auto *D = dyn_cast(LC->getDecl()); // FIXME: when operator() of lambda is analyzed as a top level function and // 'this' refers to a this to the enclosing scope, there is no right region to // return. while (!LC->inTopFrame() && (!D || D->isStatic() || PT != D->getThisType(getContext())->getAs())) { LC = LC->getParent(); D = dyn_cast(LC->getDecl()); } const StackFrameContext *STC = LC->getCurrentStackFrame(); assert(STC); return getSubRegion(PT, getStackArgumentsRegion(STC)); } const AllocaRegion* MemRegionManager::getAllocaRegion(const Expr *E, unsigned cnt, const LocationContext *LC) { const StackFrameContext *STC = LC->getCurrentStackFrame(); assert(STC); return getSubRegion(E, cnt, getStackLocalsRegion(STC)); } const MemSpaceRegion *MemRegion::getMemorySpace() const { const MemRegion *R = this; const SubRegion* SR = dyn_cast(this); while (SR) { R = SR->getSuperRegion(); SR = dyn_cast(R); } return dyn_cast(R); } bool MemRegion::hasStackStorage() const { return isa(getMemorySpace()); } bool MemRegion::hasStackNonParametersStorage() const { return isa(getMemorySpace()); } bool MemRegion::hasStackParametersStorage() const { return isa(getMemorySpace()); } bool MemRegion::hasGlobalsOrParametersStorage() const { const MemSpaceRegion *MS = getMemorySpace(); return isa(MS) || isa(MS); } // getBaseRegion strips away all elements and fields, and get the base region // of them. const MemRegion *MemRegion::getBaseRegion() const { const MemRegion *R = this; while (true) { switch (R->getKind()) { case MemRegion::ElementRegionKind: case MemRegion::FieldRegionKind: case MemRegion::ObjCIvarRegionKind: case MemRegion::CXXBaseObjectRegionKind: R = cast(R)->getSuperRegion(); continue; default: break; } break; } return R; } bool MemRegion::isSubRegionOf(const MemRegion *R) const { return false; } //===----------------------------------------------------------------------===// // View handling. //===----------------------------------------------------------------------===// const MemRegion *MemRegion::StripCasts(bool StripBaseCasts) const { const MemRegion *R = this; while (true) { switch (R->getKind()) { case ElementRegionKind: { const ElementRegion *ER = cast(R); if (!ER->getIndex().isZeroConstant()) return R; R = ER->getSuperRegion(); break; } case CXXBaseObjectRegionKind: if (!StripBaseCasts) return R; R = cast(R)->getSuperRegion(); break; default: return R; } } } const SymbolicRegion *MemRegion::getSymbolicBase() const { const SubRegion *SubR = dyn_cast(this); while (SubR) { if (const SymbolicRegion *SymR = dyn_cast(SubR)) return SymR; SubR = dyn_cast(SubR->getSuperRegion()); } return nullptr; } RegionRawOffset ElementRegion::getAsArrayOffset() const { CharUnits offset = CharUnits::Zero(); const ElementRegion *ER = this; const MemRegion *superR = nullptr; ASTContext &C = getContext(); // FIXME: Handle multi-dimensional arrays. while (ER) { superR = ER->getSuperRegion(); // FIXME: generalize to symbolic offsets. SVal index = ER->getIndex(); if (Optional CI = index.getAs()) { // Update the offset. int64_t i = CI->getValue().getSExtValue(); if (i != 0) { QualType elemType = ER->getElementType(); // If we are pointing to an incomplete type, go no further. if (elemType->isIncompleteType()) { superR = ER; break; } CharUnits size = C.getTypeSizeInChars(elemType); offset += (i * size); } // Go to the next ElementRegion (if any). ER = dyn_cast(superR); continue; } return nullptr; } assert(superR && "super region cannot be NULL"); return RegionRawOffset(superR, offset); } /// Returns true if \p Base is an immediate base class of \p Child static bool isImmediateBase(const CXXRecordDecl *Child, const CXXRecordDecl *Base) { assert(Child && "Child must not be null"); // Note that we do NOT canonicalize the base class here, because // ASTRecordLayout doesn't either. If that leads us down the wrong path, // so be it; at least we won't crash. for (const auto &I : Child->bases()) { if (I.getType()->getAsCXXRecordDecl() == Base) return true; } return false; } RegionOffset MemRegion::getAsOffset() const { const MemRegion *R = this; const MemRegion *SymbolicOffsetBase = nullptr; int64_t Offset = 0; while (1) { switch (R->getKind()) { case CodeSpaceRegionKind: case StackLocalsSpaceRegionKind: case StackArgumentsSpaceRegionKind: case HeapSpaceRegionKind: case UnknownSpaceRegionKind: case StaticGlobalSpaceRegionKind: case GlobalInternalSpaceRegionKind: case GlobalSystemSpaceRegionKind: case GlobalImmutableSpaceRegionKind: // Stores can bind directly to a region space to set a default value. assert(Offset == 0 && !SymbolicOffsetBase); goto Finish; case FunctionCodeRegionKind: case BlockCodeRegionKind: case BlockDataRegionKind: // These will never have bindings, but may end up having values requested // if the user does some strange casting. if (Offset != 0) SymbolicOffsetBase = R; goto Finish; case SymbolicRegionKind: case AllocaRegionKind: case CompoundLiteralRegionKind: case CXXThisRegionKind: case StringRegionKind: case ObjCStringRegionKind: case VarRegionKind: case CXXTempObjectRegionKind: // Usual base regions. goto Finish; case ObjCIvarRegionKind: // This is a little strange, but it's a compromise between // ObjCIvarRegions having unknown compile-time offsets (when using the // non-fragile runtime) and yet still being distinct, non-overlapping // regions. Thus we treat them as "like" base regions for the purposes // of computing offsets. goto Finish; case CXXBaseObjectRegionKind: { const CXXBaseObjectRegion *BOR = cast(R); R = BOR->getSuperRegion(); QualType Ty; bool RootIsSymbolic = false; if (const TypedValueRegion *TVR = dyn_cast(R)) { Ty = TVR->getDesugaredValueType(getContext()); } else if (const SymbolicRegion *SR = dyn_cast(R)) { // If our base region is symbolic, we don't know what type it really is. // Pretend the type of the symbol is the true dynamic type. // (This will at least be self-consistent for the life of the symbol.) Ty = SR->getSymbol()->getType()->getPointeeType(); RootIsSymbolic = true; } const CXXRecordDecl *Child = Ty->getAsCXXRecordDecl(); if (!Child) { // We cannot compute the offset of the base class. SymbolicOffsetBase = R; } else { if (RootIsSymbolic) { // Base layers on symbolic regions may not be type-correct. // Double-check the inheritance here, and revert to a symbolic offset // if it's invalid (e.g. due to a reinterpret_cast). if (BOR->isVirtual()) { if (!Child->isVirtuallyDerivedFrom(BOR->getDecl())) SymbolicOffsetBase = R; } else { if (!isImmediateBase(Child, BOR->getDecl())) SymbolicOffsetBase = R; } } } // Don't bother calculating precise offsets if we already have a // symbolic offset somewhere in the chain. if (SymbolicOffsetBase) continue; CharUnits BaseOffset; const ASTRecordLayout &Layout = getContext().getASTRecordLayout(Child); if (BOR->isVirtual()) BaseOffset = Layout.getVBaseClassOffset(BOR->getDecl()); else BaseOffset = Layout.getBaseClassOffset(BOR->getDecl()); // The base offset is in chars, not in bits. Offset += BaseOffset.getQuantity() * getContext().getCharWidth(); break; } case ElementRegionKind: { const ElementRegion *ER = cast(R); R = ER->getSuperRegion(); QualType EleTy = ER->getValueType(); if (EleTy->isIncompleteType()) { // We cannot compute the offset of the base class. SymbolicOffsetBase = R; continue; } SVal Index = ER->getIndex(); if (Optional CI = Index.getAs()) { // Don't bother calculating precise offsets if we already have a // symbolic offset somewhere in the chain. if (SymbolicOffsetBase) continue; int64_t i = CI->getValue().getSExtValue(); // This type size is in bits. Offset += i * getContext().getTypeSize(EleTy); } else { // We cannot compute offset for non-concrete index. SymbolicOffsetBase = R; } break; } case FieldRegionKind: { const FieldRegion *FR = cast(R); R = FR->getSuperRegion(); const RecordDecl *RD = FR->getDecl()->getParent(); if (RD->isUnion() || !RD->isCompleteDefinition()) { // We cannot compute offset for incomplete type. // For unions, we could treat everything as offset 0, but we'd rather // treat each field as a symbolic offset so they aren't stored on top // of each other, since we depend on things in typed regions actually // matching their types. SymbolicOffsetBase = R; } // Don't bother calculating precise offsets if we already have a // symbolic offset somewhere in the chain. if (SymbolicOffsetBase) continue; // Get the field number. unsigned idx = 0; for (RecordDecl::field_iterator FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI, ++idx) { if (FR->getDecl() == *FI) break; } const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD); // This is offset in bits. Offset += Layout.getFieldOffset(idx); break; } } } Finish: if (SymbolicOffsetBase) return RegionOffset(SymbolicOffsetBase, RegionOffset::Symbolic); return RegionOffset(R, Offset); } //===----------------------------------------------------------------------===// // BlockDataRegion //===----------------------------------------------------------------------===// std::pair BlockDataRegion::getCaptureRegions(const VarDecl *VD) { MemRegionManager &MemMgr = *getMemRegionManager(); const VarRegion *VR = nullptr; const VarRegion *OriginalVR = nullptr; if (!VD->hasAttr() && VD->hasLocalStorage()) { VR = MemMgr.getVarRegion(VD, this); OriginalVR = MemMgr.getVarRegion(VD, LC); } else { if (LC) { VR = MemMgr.getVarRegion(VD, LC); OriginalVR = VR; } else { VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion()); OriginalVR = MemMgr.getVarRegion(VD, LC); } } return std::make_pair(VR, OriginalVR); } void BlockDataRegion::LazyInitializeReferencedVars() { if (ReferencedVars) return; AnalysisDeclContext *AC = getCodeRegion()->getAnalysisDeclContext(); const auto &ReferencedBlockVars = AC->getReferencedBlockVars(BC->getDecl()); auto NumBlockVars = std::distance(ReferencedBlockVars.begin(), ReferencedBlockVars.end()); if (NumBlockVars == 0) { ReferencedVars = (void*) 0x1; return; } MemRegionManager &MemMgr = *getMemRegionManager(); llvm::BumpPtrAllocator &A = MemMgr.getAllocator(); BumpVectorContext BC(A); typedef BumpVector VarVec; VarVec *BV = A.Allocate(); new (BV) VarVec(BC, NumBlockVars); VarVec *BVOriginal = A.Allocate(); new (BVOriginal) VarVec(BC, NumBlockVars); for (const VarDecl *VD : ReferencedBlockVars) { const VarRegion *VR = nullptr; const VarRegion *OriginalVR = nullptr; std::tie(VR, OriginalVR) = getCaptureRegions(VD); assert(VR); assert(OriginalVR); BV->push_back(VR, BC); BVOriginal->push_back(OriginalVR, BC); } ReferencedVars = BV; OriginalVars = BVOriginal; } BlockDataRegion::referenced_vars_iterator BlockDataRegion::referenced_vars_begin() const { const_cast(this)->LazyInitializeReferencedVars(); BumpVector *Vec = static_cast*>(ReferencedVars); if (Vec == (void*) 0x1) return BlockDataRegion::referenced_vars_iterator(nullptr, nullptr); BumpVector *VecOriginal = static_cast*>(OriginalVars); return BlockDataRegion::referenced_vars_iterator(Vec->begin(), VecOriginal->begin()); } BlockDataRegion::referenced_vars_iterator BlockDataRegion::referenced_vars_end() const { const_cast(this)->LazyInitializeReferencedVars(); BumpVector *Vec = static_cast*>(ReferencedVars); if (Vec == (void*) 0x1) return BlockDataRegion::referenced_vars_iterator(nullptr, nullptr); BumpVector *VecOriginal = static_cast*>(OriginalVars); return BlockDataRegion::referenced_vars_iterator(Vec->end(), VecOriginal->end()); } const VarRegion *BlockDataRegion::getOriginalRegion(const VarRegion *R) const { for (referenced_vars_iterator I = referenced_vars_begin(), E = referenced_vars_end(); I != E; ++I) { if (I.getCapturedRegion() == R) return I.getOriginalRegion(); } return nullptr; } //===----------------------------------------------------------------------===// // RegionAndSymbolInvalidationTraits //===----------------------------------------------------------------------===// void RegionAndSymbolInvalidationTraits::setTrait(SymbolRef Sym, InvalidationKinds IK) { SymTraitsMap[Sym] |= IK; } void RegionAndSymbolInvalidationTraits::setTrait(const MemRegion *MR, InvalidationKinds IK) { assert(MR); if (const SymbolicRegion *SR = dyn_cast(MR)) setTrait(SR->getSymbol(), IK); else MRTraitsMap[MR] |= IK; } bool RegionAndSymbolInvalidationTraits::hasTrait(SymbolRef Sym, InvalidationKinds IK) const { const_symbol_iterator I = SymTraitsMap.find(Sym); if (I != SymTraitsMap.end()) return I->second & IK; return false; } bool RegionAndSymbolInvalidationTraits::hasTrait(const MemRegion *MR, InvalidationKinds IK) const { if (!MR) return false; if (const SymbolicRegion *SR = dyn_cast(MR)) return hasTrait(SR->getSymbol(), IK); const_region_iterator I = MRTraitsMap.find(MR); if (I != MRTraitsMap.end()) return I->second & IK; return false; } diff --git a/clang/test/Analysis/MPIMock.h b/clang/test/Analysis/MPIMock.h new file mode 100644 index 000000000000..01d2d42fc58a --- /dev/null +++ b/clang/test/Analysis/MPIMock.h @@ -0,0 +1,55 @@ +// Message Passing Interface mock header. Mocks MPI constants and functions, in +// order to make them available in distinct integration test files. + +#define NULL 0 + +// mock types +typedef int MPI_Datatype; +typedef int MPI_Comm; +typedef int MPI_Request; +typedef int MPI_Status; +typedef int MPI_Op; +typedef int int8_t; +typedef int uint8_t; +typedef int uint16_t; +typedef int int64_t; +namespace std { template struct complex { T real; T imag; }; } + +// mock constants +#define MPI_DATATYPE_NULL 0 +#define MPI_CHAR 0 +#define MPI_BYTE 0 +#define MPI_INT 0 +#define MPI_LONG 0 +#define MPI_LONG_DOUBLE 0 +#define MPI_UNSIGNED 0 +#define MPI_INT8_T 0 +#define MPI_UINT8_T 0 +#define MPI_UINT16_T 0 +#define MPI_C_LONG_DOUBLE_COMPLEX 0 +#define MPI_FLOAT 0 +#define MPI_DOUBLE 0 +#define MPI_CXX_BOOL 0 +#define MPI_CXX_FLOAT_COMPLEX 0 +#define MPI_CXX_DOUBLE_COMPLEX 0 +#define MPI_CXX_LONG_DOUBLE_COMPLEX 0 +#define MPI_IN_PLACE 0 +#define MPI_COMM_WORLD 0 +#define MPI_STATUS_IGNORE 0 +#define MPI_STATUSES_IGNORE 0 +#define MPI_SUM 0 + +// mock functions +int MPI_Comm_size(MPI_Comm, int *); +int MPI_Comm_rank(MPI_Comm, int *); +int MPI_Send(const void *, int, MPI_Datatype, int, int, MPI_Comm); +int MPI_Recv(void *, int, MPI_Datatype, int, int, MPI_Comm, MPI_Status *); +int MPI_Isend(const void *, int, MPI_Datatype, int, int, MPI_Comm, + MPI_Request *); +int MPI_Irecv(void *, int, MPI_Datatype, int, int, MPI_Comm, MPI_Request *); +int MPI_Wait(MPI_Request *, MPI_Status *); +int MPI_Waitall(int, MPI_Request[], MPI_Status[]); +int MPI_Reduce(const void *, void *, int, MPI_Datatype, MPI_Op, int, MPI_Comm); +int MPI_Ireduce(const void *, void *, int, MPI_Datatype, MPI_Op, int, MPI_Comm, + MPI_Request *); +int MPI_Bcast(void *, int count, MPI_Datatype, int, MPI_Comm); diff --git a/clang/test/Analysis/MemRegion.cpp b/clang/test/Analysis/MemRegion.cpp new file mode 100644 index 000000000000..992b7f1f9767 --- /dev/null +++ b/clang/test/Analysis/MemRegion.cpp @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=optin.mpi.MPI-Checker -verify %s + +#include "MPIMock.h" + +// Use MPI-Checker to test 'getDescriptiveName', as the checker uses the +// function for diagnostics. +void testGetDescriptiveName() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Request sendReq1; + MPI_Wait(&sendReq1, MPI_STATUS_IGNORE); // expected-warning{{Request 'sendReq1' has no matching nonblocking call.}} +} + +void testGetDescriptiveName2() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Request sendReq1[10][10][10]; + MPI_Wait(&sendReq1[1][7][9], MPI_STATUS_IGNORE); // expected-warning{{Request 'sendReq1[1][7][9]' has no matching nonblocking call.}} +} + +void testGetDescriptiveName3() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + typedef struct { MPI_Request req; } ReqStruct; + ReqStruct rs; + MPI_Request *r = &rs.req; + MPI_Wait(r, MPI_STATUS_IGNORE); // expected-warning{{Request 'rs.req' has no matching nonblocking call.}} +} + +void testGetDescriptiveName4() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + typedef struct { MPI_Request req[2][2]; } ReqStruct; + ReqStruct rs; + MPI_Request *r = &rs.req[0][1]; + MPI_Wait(r, MPI_STATUS_IGNORE); // expected-warning{{Request 'rs.req[0][1]' has no matching nonblocking call.}} +} + +void testGetDescriptiveName5() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + typedef struct { MPI_Request req; } ReqStructInner; + typedef struct { ReqStructInner req; } ReqStruct; + ReqStruct rs; + MPI_Request *r = &rs.req.req; + MPI_Wait(r, MPI_STATUS_IGNORE); // expected-warning{{Request 'rs.req.req' has no matching nonblocking call.}} +} diff --git a/clang/test/Analysis/mpichecker.cpp b/clang/test/Analysis/mpichecker.cpp new file mode 100644 index 000000000000..b7a1e00e00b4 --- /dev/null +++ b/clang/test/Analysis/mpichecker.cpp @@ -0,0 +1,342 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=optin.mpi.MPI-Checker -verify %s + +#include "MPIMock.h" + +void matchedWait1() { + int rank = 0; + double buf = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank >= 0) { + MPI_Request sendReq1, recvReq1; + MPI_Isend(&buf, 1, MPI_DOUBLE, rank + 1, 0, MPI_COMM_WORLD, &sendReq1); + MPI_Irecv(&buf, 1, MPI_DOUBLE, rank - 1, 0, MPI_COMM_WORLD, &recvReq1); + + MPI_Wait(&sendReq1, MPI_STATUS_IGNORE); + MPI_Wait(&recvReq1, MPI_STATUS_IGNORE); + } +} // no error + +void matchedWait2() { + int rank = 0; + double buf = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank >= 0) { + MPI_Request sendReq1, recvReq1; + MPI_Isend(&buf, 1, MPI_DOUBLE, rank + 1, 0, MPI_COMM_WORLD, &sendReq1); + MPI_Irecv(&buf, 1, MPI_DOUBLE, rank - 1, 0, MPI_COMM_WORLD, &recvReq1); + MPI_Wait(&sendReq1, MPI_STATUS_IGNORE); + MPI_Wait(&recvReq1, MPI_STATUS_IGNORE); + } +} // no error + +void matchedWait3() { + int rank = 0; + double buf = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank >= 0) { + MPI_Request sendReq1, recvReq1; + MPI_Isend(&buf, 1, MPI_DOUBLE, rank + 1, 0, MPI_COMM_WORLD, &sendReq1); + MPI_Irecv(&buf, 1, MPI_DOUBLE, rank - 1, 0, MPI_COMM_WORLD, &recvReq1); + + if (rank > 1000) { + MPI_Wait(&sendReq1, MPI_STATUS_IGNORE); + MPI_Wait(&recvReq1, MPI_STATUS_IGNORE); + } else { + MPI_Wait(&sendReq1, MPI_STATUS_IGNORE); + MPI_Wait(&recvReq1, MPI_STATUS_IGNORE); + } + } +} // no error + +void missingWait1() { // Check missing wait for dead region. + double buf = 0; + MPI_Request sendReq1; + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, &sendReq1); +} // expected-warning{{Request 'sendReq1' has no matching wait.}} + +void missingWait2() { + int rank = 0; + double buf = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank == 0) { + } else { + MPI_Request sendReq1, recvReq1; + + MPI_Isend(&buf, 1, MPI_DOUBLE, rank + 1, 0, MPI_COMM_WORLD, &sendReq1); + MPI_Irecv(&buf, 1, MPI_DOUBLE, rank - 1, 0, MPI_COMM_WORLD, &recvReq1); // expected-warning{{Request 'sendReq1' has no matching wait.}} + MPI_Wait(&recvReq1, MPI_STATUS_IGNORE); + } +} + +void doubleNonblocking() { + int rank = 0; + double buf = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank == 1) { + } else { + MPI_Request sendReq1; + + MPI_Isend(&buf, 1, MPI_DOUBLE, rank + 1, 0, MPI_COMM_WORLD, &sendReq1); + MPI_Irecv(&buf, 1, MPI_DOUBLE, rank - 1, 0, MPI_COMM_WORLD, &sendReq1); // expected-warning{{Double nonblocking on request 'sendReq1'.}} + MPI_Wait(&sendReq1, MPI_STATUS_IGNORE); + } +} + +void doubleNonblocking2() { + int rank = 0; + double buf = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + MPI_Request req; + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, &req); + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, &req); // expected-warning{{Double nonblocking on request 'req'.}} + MPI_Wait(&req, MPI_STATUS_IGNORE); +} + +void doubleNonblocking3() { + typedef struct { MPI_Request req; } ReqStruct; + + ReqStruct rs; + int rank = 0; + double buf = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, &rs.req); + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, &rs.req); // expected-warning{{Double nonblocking on request 'rs.req'.}} + MPI_Wait(&rs.req, MPI_STATUS_IGNORE); +} + +void doubleNonblocking4() { + int rank = 0; + double buf = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + MPI_Request req; + for (int i = 0; i < 2; ++i) { + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, &req); // expected-warning{{Double nonblocking on request 'req'.}} + } + MPI_Wait(&req, MPI_STATUS_IGNORE); +} + +void tripleNonblocking() { + double buf = 0; + MPI_Request sendReq; + MPI_Isend(&buf, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &sendReq); + MPI_Irecv(&buf, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &sendReq); // expected-warning{{Double nonblocking on request 'sendReq'.}} + MPI_Isend(&buf, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &sendReq); // expected-warning{{Double nonblocking on request 'sendReq'.}} + MPI_Wait(&sendReq, MPI_STATUS_IGNORE); +} + +void missingNonBlocking() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Request sendReq1[10][10][10]; + MPI_Wait(&sendReq1[1][7][9], MPI_STATUS_IGNORE); // expected-warning{{Request 'sendReq1[1][7][9]' has no matching nonblocking call.}} +} + +void missingNonBlocking2() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + typedef struct { MPI_Request req[2][2]; } ReqStruct; + ReqStruct rs; + MPI_Request *r = &rs.req[0][1]; + MPI_Wait(r, MPI_STATUS_IGNORE); // expected-warning{{Request 'rs.req[0][1]' has no matching nonblocking call.}} +} + +void missingNonBlocking3() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Request sendReq; + MPI_Wait(&sendReq, MPI_STATUS_IGNORE); // expected-warning{{Request 'sendReq' has no matching nonblocking call.}} +} + +void missingNonBlockingMultiple() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Request sendReq[4]; + for (int i = 0; i < 4; ++i) { + MPI_Wait(&sendReq[i], MPI_STATUS_IGNORE); // expected-warning-re 1+{{Request {{.*}} has no matching nonblocking call.}} + } +} + +void missingNonBlockingWaitall() { + int rank = 0; + double buf = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Request req[4]; + + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &req[0]); + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &req[1]); + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &req[3]); + + MPI_Waitall(4, req, MPI_STATUSES_IGNORE); // expected-warning{{Request 'req[2]' has no matching nonblocking call.}} +} + +void missingNonBlockingWaitall2() { + int rank = 0; + double buf = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Request req[4]; + + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &req[0]); + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &req[3]); + + MPI_Waitall(4, req, MPI_STATUSES_IGNORE); // expected-warning-re 2{{Request '{{(.*)[[1-2]](.*)}}' has no matching nonblocking call.}} +} + +void missingNonBlockingWaitall3() { + int rank = 0; + double buf = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Request req[4]; + + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &req[0]); + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &req[2]); + + MPI_Waitall(4, req, MPI_STATUSES_IGNORE); // expected-warning-re 2{{Request '{{(.*)[[1,3]](.*)}}' has no matching nonblocking call.}} +} + +void missingNonBlockingWaitall4() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Request req[4]; + MPI_Waitall(4, req, MPI_STATUSES_IGNORE); // expected-warning-re 4{{Request '{{(.*)[[0-3]](.*)}}' has no matching nonblocking call.}} +} + +void noDoubleRequestUsage() { + typedef struct { + MPI_Request req; + MPI_Request req2; + } ReqStruct; + + ReqStruct rs; + int rank = 0; + double buf = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &rs.req); + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &rs.req2); + MPI_Wait(&rs.req, MPI_STATUS_IGNORE); + MPI_Wait(&rs.req2, MPI_STATUS_IGNORE); +} // no error + +void noDoubleRequestUsage2() { + typedef struct { + MPI_Request req[2]; + MPI_Request req2; + } ReqStruct; + + ReqStruct rs; + int rank = 0; + double buf = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &rs.req[0]); + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &rs.req[1]); + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &rs.req2); + MPI_Wait(&rs.req[0], MPI_STATUS_IGNORE); + MPI_Wait(&rs.req[1], MPI_STATUS_IGNORE); + MPI_Wait(&rs.req2, MPI_STATUS_IGNORE); +} // no error + +void nestedRequest() { + typedef struct { + MPI_Request req[2]; + MPI_Request req2; + } ReqStruct; + + ReqStruct rs; + int rank = 0; + double buf = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &rs.req[0]); + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &rs.req[1]); + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &rs.req2); + MPI_Waitall(2, rs.req, MPI_STATUSES_IGNORE); + MPI_Wait(&rs.req2, MPI_STATUS_IGNORE); +} // no error + +void singleRequestInWaitall() { + MPI_Request r; + int rank = 0; + double buf = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &r); + MPI_Waitall(1, &r, MPI_STATUSES_IGNORE); +} // no error + +void multiRequestUsage() { + double buf = 0; + MPI_Request req; + + MPI_Isend(&buf, 1, MPI_DOUBLE, 1, 0, MPI_COMM_WORLD, &req); + MPI_Wait(&req, MPI_STATUS_IGNORE); + + MPI_Irecv(&buf, 1, MPI_DOUBLE, 1, 0, MPI_COMM_WORLD, &req); + MPI_Wait(&req, MPI_STATUS_IGNORE); +} // no error + +void multiRequestUsage2() { + double buf = 0; + MPI_Request req; + + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &req); + MPI_Wait(&req, MPI_STATUS_IGNORE); + + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &req); + MPI_Wait(&req, MPI_STATUS_IGNORE); +} // no error + +// wrapper function +void callNonblocking(MPI_Request *req) { + double buf = 0; + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + req); +} + +// wrapper function +void callWait(MPI_Request *req) { + MPI_Wait(req, MPI_STATUS_IGNORE); +} + +// Call nonblocking, wait wrapper functions. +void callWrapperFunctions() { + MPI_Request req; + callNonblocking(&req); + callWait(&req); +} // no error + +void externFunctions1() { + double buf = 0; + MPI_Request req; + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, + &req); + void callWaitExtern(MPI_Request *req); + callWaitExtern(&req); +} // expected-warning{{Request 'req' has no matching wait.}} + +void externFunctions2() { + MPI_Request req; + void callNonblockingExtern(MPI_Request *req); + callNonblockingExtern(&req); +} diff --git a/clang/test/Analysis/mpicheckernotes.cpp b/clang/test/Analysis/mpicheckernotes.cpp new file mode 100644 index 000000000000..be312fdf5fda --- /dev/null +++ b/clang/test/Analysis/mpicheckernotes.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=optin.mpi.MPI-Checker -analyzer-output=text -verify %s + +// MPI-Checker test file to test note diagnostics. + +#include "MPIMock.h" + +void doubleNonblocking() { + double buf = 0; + MPI_Request sendReq; + MPI_Isend(&buf, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &sendReq); // expected-note{{Request is previously used by nonblocking call here.}} + MPI_Irecv(&buf, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &sendReq); // expected-warning{{Double nonblocking on request 'sendReq'.}} expected-note{{Double nonblocking on request 'sendReq'.}} + MPI_Wait(&sendReq, MPI_STATUS_IGNORE); +} + +void missingWait() { + double buf = 0; + MPI_Request sendReq; + MPI_Ireduce(MPI_IN_PLACE, &buf, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD, &sendReq); // expected-note{{Request is previously used by nonblocking call here.}} +} // expected-warning{{Request 'sendReq' has no matching wait.}} expected-note{{Request 'sendReq' has no matching wait.}} + +// If more than 2 nonblocking calls are using a request in a sequence, they all +// point to the first call as the 'previous' call. This is because the +// BugReporterVisitor only checks for differences in state or existence of an +// entity. +void tripleNonblocking() { + double buf = 0; + MPI_Request sendReq; + MPI_Isend(&buf, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &sendReq); // expected-note 2{{Request is previously used by nonblocking call here.}} + MPI_Irecv(&buf, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &sendReq); // expected-warning{{Double nonblocking on request 'sendReq'.}} expected-note{{Double nonblocking on request 'sendReq'.}} + + MPI_Isend(&buf, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &sendReq); // expected-warning{{Double nonblocking on request 'sendReq'.}} expected-note{{Double nonblocking on request 'sendReq'.}} + + MPI_Wait(&sendReq, MPI_STATUS_IGNORE); +}