• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
OpenMP and static code analysis
 

OpenMP and static code analysis

on

  • 679 views

The article describes principles on which implementation of the static code analyzer VivaMP is based. The described set of testing logical conditions allows you to diagnose some errors in parallel ...

The article describes principles on which implementation of the static code analyzer VivaMP is based. The described set of testing logical conditions allows you to diagnose some errors in parallel programs created on the basis of OpenMP technology.

Statistics

Views

Total Views
679
Views on SlideShare
679
Embed Views
0

Actions

Likes
0
Downloads
5
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    OpenMP and static code analysis OpenMP and static code analysis Document Transcript

    • OpenMP and static code analysisAuthors: Andrey Karpov, Evgeniy RyzhkovDate: 20.11.2009AbstractThe article describes principles on which implementation of the static code analyzer VivaMP is based.The described set of testing logical conditions allows you to diagnose some errors in parallel programscreated on the basis of OpenMP technology.IntroductionMany errors in the programs developed on the basis of OpenMP technology can be diagnosed with thehelp of the static code analyzer [1]. This article gives a set of diagnostic rules detecting potentiallyunsafe code sections which most likely contain errors. The rules described below are meant for testing Cand C++ code. But many rules can be applied to the Fortran programs as well after some modification.The rules described in the article are the basis of the static code analyzer VivaMP developed by OOO"Program Verification Systems" [2]. VivaMP analyzer is intended for testing code of C/C++ applications.The analyzer integrates into Visual Studio 2005/2008 development environments and also adds adocumentation section into MSDN Help system. VivaMP code analyzer is included in PVS-Studiosoftware product.This article is being renewed and modified together with the development of PVS-Studio. At present,this is the third variant of the article, which refers to PVS-Studio 3.40. If it is mentioned in the article thatsome diagnostic features are not implemented, this refers to PVS-Studio 3.40. In the following versionsof the analyzer, such features can be implemented. See a newer variant of this article or PVS-Studiodocumentation.Diagnostic rulesIt is considered that the directives in all the rules are used together with "omp" directive, if not statedotherwise. That is, we omit mentioning that "omp" directive is used in order to shorten the rules text.Generic exception AThis exception is used in several rules and is singled out to shorten their description. In general, thepoint is that we operate outside a parallel section or explicitly point which thread is used or shield thecode with lockups.We can consider a case safe when one of the following conditions is satisfied for the code being tested: 1. The parallel section is absent (there is no "parallel" directive). 2. A critical section defined by "critical" directive is used inside the parallel section. 3. There is a "master"-block inside the parallel section. 4. There is a "single"-block inside the parallel section. 5. There is an "ordered"-block inside the parallel section.
    • 6. Inside the parallel section the omp_set_lock function is used and blocking is set.Rule N1We should consider unsafe using "for" and "sections" directives without "parallel" directive.Exceptions:"for" or "sections" directives are located inside the parallel section defined by "parallel" directive.An example of unsafe code:#pragma omp forfor(int i = 0; i < 100; i++) ...An example of safe code:#pragma omp parallel{ #pragma omp for for(int i = 0; i < 100; i++) ...}Diagnostic messages based on this rule:V1001. Missing parallel keyword.Rule N2We should consider unsafe using one of the directives relating to OpenMP without "omp" directive.Exceptions:Using "warning" directive.An example of unsafe code:#pragma singleAn example of safe code:#pragma warning(disable : 4793)Diagnostic messages based on this rule:V1002. Missing omp keyword.
    • Rule N3We should consider unsafe using for operator immediately after "parallel" directive without "for"directive.An example of unsafe code:#pragma omp parallel num_threads(2)for(int i = 0; i < 2; i++) ...An example of safe code:#pragma omp parallel num_threads(2){ for(int i = 0; i < 2; i++) ...}Diagnostic messages based on this rule:V1003. Missing for keyword. Each thread will execute the entire loop.Rule N4We should consider unsafe creating a parallel loop with "parallel" and "for" directives inside a parallelsection created by "parallel" directive.An example of unsafe code:#pragma omp parallel{ #pragma omp parallel for for(int i = 0; i < 100; i++) ...}An example of safe code:#pragma omp parallel{ #pragma omp for for(int i = 0; i < 100; i++) ...
    • }Diagnostic messages based on this rule:V1004. Nested parallelization of a for loop.Rule N5We should consider unsafe using "for" and "ordered" directives together if after that "ordered" directiveis not used inside the loop defined by for operator.An example of unsafe code:#pragma omp parallel for orderedfor(int i = 0; i < 4; i++){ foo(i);}An example of safe code:#pragma omp parallel for orderedfor(int i = 0; i < 4; i++){ #pragma omp ordered { foo(i); }}Diagnostic messages based on this rule:V1005. The ordered directive is not present in an ordered loop.Rule N6We should consider unsafe call of omp_set_num_threads function inside a parallel section defined by"parallel" directive.An example of unsafe code:#pragma omp parallel{ omp_set_num_threads(2);}
    • An example of safe code:omp_set_num_threads(2);#pragma omp parallel{ ...}Diagnostic messages based on this rule:V1101. Redefining number of threads in a parallel code.Rule N7We should consider unsafe odd use of omp_set_lock, omp_set_nest_lock, omp_unset_lock andomp_unset_nest_lock functions inside a parallel section.An example of unsafe code:#pragma omp parallel sections{ #pragma omp section { omp_set_lock(&myLock); }}An example of safe code:#pragma omp parallel sections{ #pragma omp section { omp_set_lock(&myLock); omp_unset_lock(&myLock); }}Diagnostic messages based on this rule:V1102. Non-symmetrical use of set/unset functions for the following lock variable(s): %1%.
    • Rule N8We should consider unsafe using omp_get_num_threads function in arithmetic operations.Exceptions:The returned value of omp_get_num_threads function is used for comparing or equating with avariable.An example of unsafe code:int lettersPerThread = 26 / omp_get_num_threads();An example of safe code:bool b = omp_get_num_threads() == 2;switch(omp_get_num_threads()){ ...}Diagnostic messages based on this rule:V1103. Threads number dependent code. The omp_get_num_threads function is used in an arithmeticexpresion.Rule N9We should consider unsafe call of omp_set_nested function inside a parallel section defined by"parallel" directive.Exceptions:The function is located in an embedded block created by "master" or "single" directive.An example of unsafe code:#pragma omp parallel{ omp_set_nested(2);}An example of safe code:#pragma omp parallel{ #pragma omp master
    • { omp_set_nested(2); }}Diagnostic messages based on this rule:V1104. Redefining nested parallelism in a parallel code.Rule N10We should consider unsafe functions using common resources. An example of such functions is printf.Exceptions:Generic exception A.An example of unsafe code:#pragma omp parallel{ printf("abcd");}An example of safe code:#pragma omp parallel{ #pragma omp critical { printf("abcd"); }}Diagnostic messages based on this rule:V1201. Concurrent usage of a shared resource via an unprotected call of the %1% function.Rule N11We should consider unsafe applying flush directive to pointers.An example of unsafe code:int *t;...
    • #pragma omp flush(t)An example of safe code:int t;...#pragma omp flush(t)Diagnostic messages based on this rule:V1202. The flush directive should not be used for the %1% variable, because the variable has pointertype.Rule N12We should consider unsafe using "threadprivate" directive.An example of unsafe code:#pragma omp threadprivate(var)Diagnostic messages based on this rule:V1203. Using the threadprivate directive is dangerous, because it affects the entire file. Use localvariables or specify access type for each parallel block explicitly instead.Rule N13We should consider unsafe initialization or modification of an object (variable) in a parallel section if theobject is global (common for the threads) in relation to this section.Explanation of the rule:The following objects are global in relation to a parallel section: 1. Static variables. 2. Static class members (in the current version of VivaMP, this rule is not implemented). 3. Variables defined outside the parallel section. 4. During the analysis of the code of the function which is called in a parallel way, global variables are considered global objects. If the analyzed function is a class member, then to consider the class members global or not depends on the way the call of this function is carried out. If the call is carried out from another function of the given class, the members of the class are considered global. If the call is carried out with the help of operator . or ->, the objects are also considered global. 5. The last item needs explaining. Let us site an example:class MyClass {public:int m_a;void IncFoo() { a++; }
    • void Foo() {#pragma omp parallel forfor (int i = 0; i < 10; i++)IncFoo(); // Variant. A}};MyClass object_1;#pragma omp parallel forfor (int i = 0; i < 10; i++){object_1.IncFoo(); // Variant. BMyClass object_2;object_2.IncFoo(); // Variant. C 1. } 2. In the case of variant A, we will consider the class members common, i.e. they are global in reference to function IncFoo. As a result, we will detect an error of race condition inside function IncFoo. 3. In case of variant B, we will consider the class members local, and there is no error in IncFoo. However, there will be given a warning that a nonstick method of IncFoo is called in a parallel way from MyClass class. This will allow to find the error. 4. In the case of variant C, we will consider that the class members are local and that there is no error in IncFoo. And there are really no errors.The object can be both of simple type and direct instance. The following operations relate to operationsof object change: 1. Transfer of an object into a function by the non-constant link. 2. Transfer of an object into a function by the non-constant pointer (In the current version of VivaMP, this rule is not implemented). 3. Change of an object during arithmetic operations or assignment operations. 4. Call of a non-constant method in the object.Excpetions:1. Generic exception A.2. "threadprivate", "private", "firstprivate", "lastprivate" or "reduction" directives are applied to the object. This exception doesnt concern static variables and static class fields which are always generic.3. Modification of an object is protected by "atomic" directive.4. Modification of an object is performed inside of only one section defined by "section" directive.
    • 5. Initialization or modification of objects is implemented inside parallelized for operator (inside the operator itself and not inside the loops body). Such objects are automatically considered private according to OpenMP specification. An example: int i; ... #pragma omp parallel for for (i = 0; i < n; i++) {}. // i - is private.An example of unsafe code:#pragma omp parallel{ static int st = 1; // V1204}void foo(int &) {}...int value;MyObjectType obj;#pragma omp parallel forfor(int i = 0; i < 33; i++){ ++value; // V1205 foo(value); // V1206 obj.non_const_foo(); // V1207}An example of safe code:#pragma omp parallel{ #pragma omp critical { static int st = 1; }}void foo(const int &) {}
    • ...int value;MyObjectType obj;#pragma omp parallel forfor(int i = 0; i < 33; i++){ #pragma omp atomic ++value; foo(value); obj.const_foo();}Diagnostic messages based on this rule:V1204. Data race risk. Unprotected static variable declaration in a parallel code.V1205. Data race risk. Unprotected concurrent operation with the %1% variable.V1206. Data race risk. The value of the %1% variable can be changed concurrently via the %2%function.V1207. Data race risk. The %1% object can be changed concurrently by a non-const function.Rule N14We should consider unsafe applying "private", "firstprivate" and "threadprivate" directives to links andpointers (not arrays).An example of unsafe code:int *arr;#pragma omp parallel for private(arr)An example of safe code:int arr[4];#pragma omp parallel for private(arr)Diagnostic messages based on this rule:V1208. The %1% variable of reference type cannot be private.V1209. Warning: The %1% variable of pointer type should not be private.
    • Rule N15We should consider unsafe absence of modification of a variable marked by "lastprivate" directive in thelast section ("section ").Exceptions:The variable is not modified in all the other sections as well.An example of unsafe code:#pragma omp sections lastprivate(a){ #pragma omp section { a = 10; } #pragma omp section { }}An example of safe code:#pragma omp sections lastprivate(a){ #pragma omp section { a = 10; } #pragma omp section { a = 20; }}Diagnostic messages based on this rule:V1210. The %1% variable is marked as lastprivate but is not changed in the last section.
    • Rule N16We should consider unsafe using a variable of omp_lock_t / omp_nest_lock_t type without its beinginitialized beforehand inside omp_init_lock / omp_init_nest_lock function.Under using we understand call of omp_set_lock function etc.An example of unsafe code:omp_lock_t myLock;#pragma omp parallel num_threads(2){ ... omp_set_lock(&myLock);}An example of safe code:omp_lock_t myLock;omp_init_lock(&myLock);#pragma omp parallel num_threads(2){ ... omp_set_lock(&myLock);}Diagnostic messages based on this rule:In the current version of VivaMP, this rule is not implemented.Rule N17We should consider unsafe using variables defined in a parallel section as private with use of "private"and "lastprivate" without their being initialized beforehand.An example of unsafe code:int a = 0;#pragma omp parallel private(a){ a++;}An example of safe code:
    • int a = 0;#pragma omp parallel private(a){ a = 0; a++;}Diagnostic messages based on this rule:In the current version of VivaMP, this rule is not implemented.Rule N18We should consider unsafe the case when after a parallel section variables are used to which "private","threadprivate" or "firstprivate" directive was applied without its being initialized beforehand.An example of unsafe code:#pragma omp parallel private(a){ ...}a++;An example of safe code:#pragma omp parallel private(a){ ...}a = 10;Diagnostic messages based on this rule:In the current version of VivaMP, this rule is not implemented.Rule N19We should consider unsafe applying "firstprivate" and "lastprivate" directives to direct instances inwhich copy constructor is absent.Diagnostic messages based on this rule:In the current version of VivaMP, this rule is not implemented.
    • Rule N20We should consider ineffective using "flush" directive where it is executed implicitly. The cases where"flush" directive is implicit and there is no sense in using it: • In Barrier directive • When critical directive enters and leaves the parallel section • When ordered directive enters and leaves the parallel section • When parallel directive enters and leaves the parallel section • When for directive leaves the parallel section • When sections directive leaves the parallel section • When single directive leaves the parallel section • When parallel for directive enters and leaves the parallel section • When parallel sections directive enters and leaves the parallel sectionDiagnostic messages based on this rule:In the current version of VivaMP, this rule is not implemented.Rule N21We should consider ineffective the use of flush directive for local variables (declared in the parallelsection), as well as of variables marked as threadprivate, private, lastprivate, and firstprivate.Example:int a = 1;#pragma omp parallel for private(a)for (int i = 10; i < 100; ++i) { #pragma omp flush(a); ...}Diagnostic messages based on this rule:V1211. The use of flush directive has no sense for private NN variable, and can reduce performance.Rule N22We should consider ineffective using critical sections or functions of omp_set_lock class where "atomic"directive is enough.Diagnostic messages based on this rule:In the current version of VivaMP, this rule is not implemented.Rule N23We should consider ineffective using flush directive for local variables (declared in a parallel section) andalso for variables marked as threadprivate, private, lastprivate, firstprivate.
    • flush directive has no sense for the enumerated variables because they always contain actual values. Butit reduces the code performance.An example of unsafe code:int a = 1;#pragma omp parallel for private(a)for (int i = 10; i < 100; ++i) { #pragma omp flush(a); ...}An example of safe code:int a = 1;#pragma omp parallel forfor (int i = 10; i < 100; ++i) { #pragma omp flush(a); ...}Diagnostic warnings based on this rule:In the current version of VivaMP, this rule is not implemented.Rule N24According to OpenMP specification, all the exceptions must be processed inside a parallel section. It isconsidered that the code generates exceptions if:throw operator is used in it;new operator is used in it;a function marked as throw(...) is called in it;Such code must be inside a try..catch block inside a parallel section.Exceptions:new operator is used that does not throw exceptions (new(std::nothrow) float[10000];).An example of unsafe code:void MyNotThrowFoo() throw() { }...
    • #pragma omp parallel for num_threads(4)for(int i = 0; i < 4; i++){ ... throw 1; ... float *ptr = new float[10000]; ... MyThrowFoo();}An example of safe code:size_t errCount = 0;#pragma omp parallel for num_threads(4) reduction(+: errCount)for(int i = 0; i < 4; i++){ try { //... throw 1; } catch (...) { ++errCount; }}if (errCount != 0) throw 1;Note: nothrow new construction is somewhat deceptive, as there can occur an impression that there canbe no exceptions here. But we should take into account that exceptions can be generated in the builderof the created objects. That means, if at least one std::string is allocated or a class itself allocatesmemory by new (without nothrow), then exceptions at call of new(nothrow) anyway can be generated.The diagnostics of the given errors lies in the analysis of builders bodies (and other objects builders
    • bodies included in the class), which are called inside parallel sections. At present, this feature is notimplemented in VivaMP.Diagnostic messages based on this rule:V1301. The throw keyword cannot be used outside of a try..catch block in a parallel section.V1302. The new operator cannot be used outside of a try..catch block in a parallel section.V1303. The %1% function which throws an exception cannot be used in a parallel section outside of atry..catch block.Rule N25We should consider unsafe not including the header file <omp.h> in the file where OpenMP directivesare used.Diagnostic messages based on this rule:V1006. Missing omp.h header file. Use #include <omp.h>.Rule N26We should consider unsafe the presence of unused variables marked in the reduction directive. This maybe evidence both of an error presence or simply that some other directive or variable was forgotten andnot removed during the process of code refactoring.Example of not using abcde variable:#pragma omp parallel for reduction (+:sum, abcde)for (i=1; i<999; i++){ sum = sum + a[i];Diagnostic messages based on this rule:In the current version of VivaMP, this rule is not implemented.Rule N27You should consider unsafe an unprotected access in a parallel loop to an array item using an indexdifferent from that used for reading.Note. The error might occur in case of a protected access as well (single, critical, ...), but for the purposeof clearness we will consider here that the protected access is safe in this case.Exceptions:1. The index is a constant.Here is an example of dangerous code:#pragma omp parallel for for (int i=2; i < 10; i++)
    • array[i] = i * array[i-1]; //V1212This is an example of safe code:#pragma omp parallel for for (int i=2; i < 10; i++) { array[i] = array [i] / 2; array_2[i] = i * array[i-1]; }Diagnostic warnings that are generated relying on this rule:V1212. Data race risk. When accessing the array %1% in a parallel loop, different indexes are used forwriting and reading.ConclusionIf you are interested in methodology of testing program code on the basis of static analysis, write us(support@viva64.com). We hope that we will find mutual interests and opportunities to collaborate!References1. Andrey Karpov. Testing parallel programs. http://www.viva64.com/art-3-2-548461019.html.2. Evgeniy Ryzhkov. VivaMP - a tool for OpenMP. http://www.viva64.com/art-3-2- 655899033.html.