You are on page 1of 19

OpenMPandstaticcodeanalysis

Abstract
Introduction
Diagnosticrules
GenericexceptionA
RuleN1
RuleN2
RuleN3
RuleN4
RuleN5
RuleN6
RuleN7
RuleN8
RuleN9
RuleN10
RuleN11
RuleN12
RuleN13
RuleN14
RuleN15
RuleN16
RuleN17
RuleN18
RuleN19
RuleN20
RuleN21
RuleN22
RuleN23
RuleN24
RuleN25
RuleN26
RuleN27
Conclusion
References

VivaMPsupporthadbeencanceledintheyear2014.Ifyouhaveany
questions,feelfreetocontactoursupport.
Abstract
Thearticledescribesprinciplesonwhichimplementationofthestaticcodeanalyzer
VivaMPisbased.Thedescribedsetoftestinglogicalconditionsallowsyouto
diagnosesomeerrorsinparallelprogramscreatedonthebasisofOpenMP
technology.

Introduction

ManyerrorsintheprogramsdevelopedonthebasisofOpenMPtechnologycanbe
diagnosedwiththehelpofthestaticcodeanalyzer[1].Thisarticlegivesasetof
diagnosticrulesdetectingpotentiallyunsafecodesectionswhichmostlikelycontain
errors.TherulesdescribedbelowaremeantfortestingCandC++code.Butmany
rulescanbeappliedtotheFortranprogramsaswellaftersomemodification.
TherulesdescribedinthearticlearethebasisofthestaticcodeanalyzerVivaMP
developedbyOOO"ProgramVerificationSystems"[2].VivaMPanalyzeris
intendedfortestingcodeofC/C++applications.TheanalyzerintegratesintoVisual
Studio2005/2008developmentenvironmentsandalsoaddsadocumentation
sectionintoMSDNHelpsystem.VivaMPcodeanalyzerisincludedinPVSStudio
softwareproduct.
ThisarticleisbeingrenewedandmodifiedtogetherwiththedevelopmentofPVS
Studio.Atpresent,thisisthethirdvariantofthearticle,whichreferstoPVSStudio
3.40.Ifitismentionedinthearticlethatsomediagnosticfeaturesarenot
implemented,thisreferstoPVSStudio3.40.Inthefollowingversionsofthe
analyzer,suchfeaturescanbeimplemented.Seeanewervariantofthisarticleor
PVSStudiodocumentation.

Diagnosticrules
Itisconsideredthatthedirectivesinalltherulesareusedtogetherwith"omp"
directive,ifnotstatedotherwise.Thatis,weomitmentioningthat"omp"directiveis
usedinordertoshortentherules'text.

GenericexceptionA
Thisexceptionisusedinseveralrulesandissingledouttoshortentheir
description.Ingeneral,thepointisthatweoperateoutsideaparallelsectionor
explicitlypointwhichthreadisusedorshieldthecodewithlockups.
Wecanconsideracasesafewhenoneofthefollowingconditionsissatisfiedfor
thecodebeingtested:
Theparallelsectionisabsent(thereisno"parallel"directive).
Acriticalsectiondefinedby"critical"directiveisusedinsidetheparallel
section.
Thereisa"master"blockinsidetheparallelsection.
Thereisa"single"blockinsidetheparallelsection.
Thereisan"ordered"blockinsidetheparallelsection.
Insidetheparallelsectiontheomp_set_lockfunctionisusedandblockingis
set.

RuleN1
Weshouldconsiderunsafeusing"for"and"sections"directiveswithout"parallel"
directive.

Exceptions:
"for"or"sections"directivesarelocatedinsidetheparallelsectiondefinedby
"parallel"directive.
Anexampleofunsafecode:
#pragma omp for
for(int i = 0; i < 100; i++)
...
Anexampleofsafecode:
#pragma omp parallel
{
#pragma omp for
for(int i = 0; i < 100; i++)
...
}
Diagnosticmessagesbasedonthisrule:
V1001.Missing'parallel'keyword.

RuleN2
WeshouldconsiderunsafeusingoneofthedirectivesrelatingtoOpenMPwithout
"omp"directive.
Exceptions:
Using"warning"directive.
Anexampleofunsafecode:
#pragma single
Anexampleofsafecode:
#pragma warning(disable : 4793)
Diagnosticmessagesbasedonthisrule:
V1002.Missing'omp'keyword.

RuleN3
Weshouldconsiderunsafeusingforoperatorimmediatelyafter"parallel"directive
without"for"directive.

Anexampleofunsafecode:
#pragma omp parallel num_threads(2)
for(int i = 0; i < 2; i++)
...
Anexampleofsafecode:
#pragma omp parallel num_threads(2)
{
for(int i = 0; i < 2; i++)
...
}
Diagnosticmessagesbasedonthisrule:
V1003.Missing'for'keyword.Eachthreadwillexecutetheentireloop.

RuleN4
Weshouldconsiderunsafecreatingaparallelloopwith"parallel"and"for"
directivesinsideaparallelsectioncreatedby"parallel"directive.
Anexampleofunsafecode:
#pragma omp parallel
{
#pragma omp parallel for
for(int i = 0; i < 100; i++)
...
}
Anexampleofsafecode:
#pragma omp parallel
{
#pragma omp for
for(int i = 0; i < 100; i++)
...
}
Diagnosticmessagesbasedonthisrule:
V1004.Nestedparallelizationofa'for'loop.

RuleN5
Weshouldconsiderunsafeusing"for"and"ordered"directivestogetherifafterthat
"ordered"directiveisnotusedinsidetheloopdefinedbyforoperator.

Anexampleofunsafecode:
#pragma omp parallel for ordered
for(int i = 0; i < 4; i++)
{
foo(i);
}
Anexampleofsafecode:
#pragma omp parallel for ordered
for(int i = 0; i < 4; i++)
{
#pragma omp ordered
{
foo(i);
}
}
Diagnosticmessagesbasedonthisrule:
V1005.The'ordered'directiveisnotpresentinanorderedloop.

RuleN6
Weshouldconsiderunsafecallofomp_set_num_threadsfunctioninsideaparallel
sectiondefinedby"parallel"directive.
Anexampleofunsafecode:
#pragma omp parallel
{
omp_set_num_threads(2);
}
Anexampleofsafecode:
omp_set_num_threads(2);
#pragma omp parallel
{
...
}
Diagnosticmessagesbasedonthisrule:
V1101.Redefiningnumberofthreadsinaparallelcode.

RuleN7
Weshouldconsiderunsafeodduseofomp_set_lock,omp_set_nest_lock,

omp_unset_lockandomp_unset_nest_lockfunctionsinsideaparallelsection.
Anexampleofunsafecode:
#pragma omp parallel sections
{
#pragma omp section
{
omp_set_lock(&myLock);
}
}
Anexampleofsafecode:
#pragma omp parallel sections
{
#pragma omp section
{
omp_set_lock(&myLock);
omp_unset_lock(&myLock);
}
}
Diagnosticmessagesbasedonthisrule:
V1102.Nonsymmetricaluseofset/unsetfunctionsforthefollowinglockvariable(s):
%1%.

RuleN8
Weshouldconsiderunsafeusingomp_get_num_threadsfunctioninarithmetic
operations.
Exceptions:
Thereturnedvalueofomp_get_num_threadsfunctionisusedforcomparingor
equatingwithavariable.
Anexampleofunsafecode:
int lettersPerThread =
26 / omp_get_num_threads();
Anexampleofsafecode:
bool b = omp_get_num_threads() == 2;
switch(omp_get_num_threads())
{
...
}

Diagnosticmessagesbasedonthisrule:
V1103.Threadsnumberdependentcode.The'omp_get_num_threads'functionis
usedinanarithmeticexpresion.

RuleN9
Weshouldconsiderunsafecallofomp_set_nestedfunctioninsideaparallelsection
definedby"parallel"directive.
Exceptions:
Thefunctionislocatedinanembeddedblockcreatedby"master"or"single"
directive.
Anexampleofunsafecode:
#pragma omp parallel
{
omp_set_nested(2);
}
Anexampleofsafecode:
#pragma omp parallel
{
#pragma omp master
{
omp_set_nested(2);
}
}
Diagnosticmessagesbasedonthisrule:
V1104.Redefiningnestedparallelisminaparallelcode.

RuleN10
Weshouldconsiderunsafefunctionsusingcommonresources.Anexampleofsuch
functionsisprintf.
Exceptions:
GenericexceptionA.
Anexampleofunsafecode:
#pragma omp parallel
{
printf("abcd");

}
Anexampleofsafecode:
#pragma omp parallel
{
#pragma omp critical
{
printf("abcd");
}
}
Diagnosticmessagesbasedonthisrule:
V1201.Concurrentusageofasharedresourceviaanunprotectedcallofthe'%1%'
function.

RuleN11
Weshouldconsiderunsafeapplyingflushdirectivetopointers.
Anexampleofunsafecode:
int *t;
...
#pragma omp flush(t)
Anexampleofsafecode:
int t;
...
#pragma omp flush(t)
Diagnosticmessagesbasedonthisrule:
V1202.The'flush'directiveshouldnotbeusedforthe'%1%'variable,becausethe
variablehaspointertype.

RuleN12
Weshouldconsiderunsafeusing"threadprivate"directive.
Anexampleofunsafecode:
#pragma omp threadprivate(var)
Diagnosticmessagesbasedonthisrule:
V1203.Usingthe'threadprivate'directiveisdangerous,becauseitaffectstheentire
file.Uselocalvariablesorspecifyaccesstypeforeachparallelblockexplicitly

instead.

RuleN13
Weshouldconsiderunsafeinitializationormodificationofanobject(variable)ina
parallelsectioniftheobjectisglobal(commonforthethreads)inrelationtothis
section.
Explanationoftherule:
Thefollowingobjectsareglobalinrelationtoaparallelsection:
Staticvariables.
Staticclassmembers(inthecurrentversionofVivaMP,thisruleisnot
implemented).
Variablesdefinedoutsidetheparallelsection.
Duringtheanalysisofthecodeofthefunctionwhichiscalledinaparallelway,
globalvariablesareconsideredglobalobjects.Iftheanalyzedfunctionisa
classmember,thentoconsidertheclassmembersglobalornotdependson
thewaythecallofthisfunctioniscarriedout.Ifthecalliscarriedoutfrom
anotherfunctionofthegivenclass,themembersoftheclassareconsidered
global.Ifthecalliscarriedoutwiththehelpofoperator'.'or'>',theobjects
arealsoconsideredglobal.
Thelastitemneedsexplaining.Letussiteanexample:
classMyClass{
public:
intm_a
voidIncFoo(){a++}
voidFoo(){
#pragmaompparallelfor
for(inti=0i<10i++)
IncFoo()//Variant.A
}
}
MyClassobject_1
#pragmaompparallelfor
for(inti=0i<10i++)

{
object_1.IncFoo()//Variant.B
MyClassobject_2
object_2.IncFoo()//Variant.C
}
InthecaseofvariantA,wewillconsidertheclassmemberscommon,i.e.they
areglobalinreferencetofunctionIncFoo.Asaresult,wewilldetectanerrorof
raceconditioninsidefunctionIncFoo.
IncaseofvariantB,wewillconsidertheclassmemberslocal,andthereisno
errorinIncFoo.However,therewillbegivenawarningthatanonstickmethod
ofIncFooiscalledinaparallelwayfromMyClassclass.Thiswillallowtofind
theerror.
InthecaseofvariantC,wewillconsiderthattheclassmembersarelocaland
thatthereisnoerrorinIncFoo.Andtherearereallynoerrors.
Theobjectcanbebothofsimpletypeanddirectinstance.Thefollowingoperations
relatetooperationsofobjectchange:
Transferofanobjectintoafunctionbythenonconstantlink.
Transferofanobjectintoafunctionbythenonconstantpointer(Inthecurrent
versionofVivaMP,thisruleisnotimplemented).
Changeofanobjectduringarithmeticoperationsorassignmentoperations.
Callofanonconstantmethodintheobject.
Excpetions:
GenericexceptionA.
"threadprivate","private","firstprivate","lastprivate"or"reduction"directives
areappliedtotheobject.Thisexceptiondoesn'tconcernstaticvariablesand
staticclassfieldswhicharealwaysgeneric.
Modificationofanobjectisprotectedby"atomic"directive.
Modificationofanobjectisperformedinsideofonlyonesectiondefinedby
"section"directive.
Initializationormodificationofobjectsisimplementedinsideparallelizedfor
operator(insidetheoperatoritselfandnotinsidetheloop'sbody).Such
objectsareautomaticallyconsideredprivateaccordingtoOpenMP
specification.Anexample:
inti
...
#pragmaompparallelfor
for(i=0i<ni++){}.//iisprivate.
Anexampleofunsafecode:
#pragma omp parallel
{

static int st = 1; // V1204


}
void foo(int &) {}
...
int value;
MyObjectType obj;
#pragma omp parallel for
for(int i = 0; i < 33; i++)
{
++value; // V1205
foo(value); // V1206
obj.non_const_foo(); // V1207
}
Anexampleofsafecode:
#pragma omp parallel
{
#pragma omp critical
{
static int st = 1;
}
}
void foo(const int &) {}
...
int value;
MyObjectType obj;
#pragma omp parallel for
for(int i = 0; i < 33; i++)
{
#pragma omp atomic
++value;
foo(value);
obj.const_foo();
}
Diagnosticmessagesbasedonthisrule:
V1204.Dataracerisk.Unprotectedstaticvariabledeclarationinaparallelcode.
V1205.Dataracerisk.Unprotectedconcurrentoperationwiththe'%1%'variable.
V1206.Dataracerisk.Thevalueofthe'%1%'variablecanbechanged
concurrentlyviathe'%2%'function.
V1207.Dataracerisk.The'%1%'objectcanbechangedconcurrentlybyanon
constfunction.

RuleN14

Weshouldconsiderunsafeapplying"private","firstprivate"and"threadprivate"
directivestolinksandpointers(notarrays).
Anexampleofunsafecode:
int *arr;
#pragma omp parallel for private(arr)
Anexampleofsafecode:
int arr[4];
#pragma omp parallel for private(arr)
Diagnosticmessagesbasedonthisrule:
V1208.The'%1%'variableofreferencetypecannotbeprivate.
V1209.Warning:The'%1%'variableofpointertypeshouldnotbeprivate.

RuleN15
Weshouldconsiderunsafeabsenceofmodificationofavariablemarkedby
"lastprivate"directiveinthelastsection("section").
Exceptions:
Thevariableisnotmodifiedinalltheothersectionsaswell.
Anexampleofunsafecode:
#pragma omp sections lastprivate(a)
{
#pragma omp section
{
a = 10;
}
#pragma omp section
{
}
}
Anexampleofsafecode:
#pragma omp sections lastprivate(a)
{
#pragma omp section
{
a = 10;
}
#pragma omp section

{
a = 20;
}
}
Diagnosticmessagesbasedonthisrule:
V1210.The'%1%'variableismarkedaslastprivatebutisnotchangedinthelast
section.

RuleN16
Weshouldconsiderunsafeusingavariableofomp_lock_t/omp_nest_lock_ttype
withoutitsbeinginitializedbeforehandinsideomp_init_lock/omp_init_nest_lock
function.
Underusingweunderstandcallofomp_set_lockfunctionetc.
Anexampleofunsafecode:
omp_lock_t myLock;
#pragma omp parallel num_threads(2)
{
...
omp_set_lock(&myLock);
}
Anexampleofsafecode:
omp_lock_t myLock;
omp_init_lock(&myLock);
#pragma omp parallel num_threads(2)
{
...
omp_set_lock(&myLock);
}
Diagnosticmessagesbasedonthisrule:
InthecurrentversionofVivaMP,thisruleisnotimplemented.

RuleN17
Weshouldconsiderunsafeusingvariablesdefinedinaparallelsectionasprivate
withuseof"private"and"lastprivate"withouttheirbeinginitializedbeforehand.
Anexampleofunsafecode:
int a = 0;
#pragma omp parallel private(a)

{
a++;
}
Anexampleofsafecode:
int a = 0;
#pragma omp parallel private(a)
{
a = 0;
a++;
}
Diagnosticmessagesbasedonthisrule:
InthecurrentversionofVivaMP,thisruleisnotimplemented.

RuleN18
Weshouldconsiderunsafethecasewhenafteraparallelsectionvariablesare
usedtowhich"private","threadprivate"or"firstprivate"directivewasappliedwithout
itsbeinginitializedbeforehand.
Anexampleofunsafecode:
#pragma omp parallel private(a)
{
...
}
a++;
Anexampleofsafecode:
#pragma omp parallel private(a)
{
...
}
a = 10;
Diagnosticmessagesbasedonthisrule:
InthecurrentversionofVivaMP,thisruleisnotimplemented.

RuleN19
Weshouldconsiderunsafeapplying"firstprivate"and"lastprivate"directivesto
directinstancesinwhichcopyconstructorisabsent.
Diagnosticmessagesbasedonthisrule:

InthecurrentversionofVivaMP,thisruleisnotimplemented.

RuleN20
Weshouldconsiderineffectiveusing"flush"directivewhereitisexecutedimplicitly.
Thecaseswhere"flush"directiveisimplicitandthereisnosenseinusingit:
InBarrierdirective
Whencriticaldirectiveentersandleavestheparallelsection
Whenordereddirectiveentersandleavestheparallelsection
Whenparalleldirectiveentersandleavestheparallelsection
Whenfordirectiveleavestheparallelsection
Whensectionsdirectiveleavestheparallelsection
Whensingledirectiveleavestheparallelsection
Whenparallelfordirectiveentersandleavestheparallelsection
Whenparallelsectionsdirectiveentersandleavestheparallelsection
Diagnosticmessagesbasedonthisrule:
InthecurrentversionofVivaMP,thisruleisnotimplemented.

RuleN21
Weshouldconsiderineffectivetheuseofflushdirectiveforlocalvariables(declared
intheparallelsection),aswellasofvariablesmarkedasthreadprivate,private,
lastprivate,andfirstprivate.
Example:
int a = 1;
#pragma omp parallel for private(a)
for (int i = 10; i < 100; ++i) {
#pragma omp flush(a);
...
}
Diagnosticmessagesbasedonthisrule:
V1211.Theuseof'flush'directivehasnosenseforprivate'NN'variable,andcan
reduceperformance.

RuleN22
Weshouldconsiderineffectiveusingcriticalsectionsorfunctionsofomp_set_lock
classwhere"atomic"directiveisenough.
Diagnosticmessagesbasedonthisrule:

InthecurrentversionofVivaMP,thisruleisnotimplemented.

RuleN23
Weshouldconsiderineffectiveusingflushdirectiveforlocalvariables(declaredina
parallelsection)andalsoforvariablesmarkedasthreadprivate,private,lastprivate,
firstprivate.
flushdirectivehasnosensefortheenumeratedvariablesbecausetheyalways
containactualvalues.Butitreducesthecodeperformance.
Anexampleofunsafecode:
int a = 1;
#pragma omp parallel for private(a)
for (int i = 10; i < 100; ++i) {
#pragma omp flush(a);
...
}
Anexampleofsafecode:
int a = 1;
#pragma omp parallel for
for (int i = 10; i < 100; ++i) {
#pragma omp flush(a);
...
}
Diagnosticwarningsbasedonthisrule:
InthecurrentversionofVivaMP,thisruleisnotimplemented.

RuleN24
AccordingtoOpenMPspecification,alltheexceptionsmustbeprocessedinsidea
parallelsection.Itisconsideredthatthecodegeneratesexceptionsif:
throwoperatorisusedinit
newoperatorisusedinit
afunctionmarkedasthrow(...)iscalledinit
Suchcodemustbeinsideatry..catchblockinsideaparallelsection.
Exceptions:
newoperatorisusedthatdoesnotthrowexceptions(new(std::nothrow)
float[10000]).

Anexampleofunsafecode:
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();
}
Anexampleofsafecode:
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:nothrownewconstructionissomewhatdeceptive,astherecanoccuran
impressionthattherecanbenoexceptionshere.Butweshouldtakeintoaccount
thatexceptionscanbegeneratedinthebuilderofthecreatedobjects.Thatmeans,
ifatleastonestd::stringisallocatedoraclassitselfallocatesmemorybynew
(withoutnothrow),thenexceptionsatcallofnew(nothrow)anywaycanbe
generated.Thediagnosticsofthegivenerrorsliesintheanalysisofbuildersbodies
(andotherobjectsbuildersbodiesincludedintheclass),whicharecalledinside
parallelsections.Atpresent,thisfeatureisnotimplementedinVivaMP.
Diagnosticmessagesbasedonthisrule:
V1301.The'throw'keywordcannotbeusedoutsideofatry..catchblockinaparallel
section.
V1302.The'new'operatorcannotbeusedoutsideofatry..catchblockinaparallel
section.

V1303.The'%1%'functionwhichthrowsanexceptioncannotbeusedinaparallel
sectionoutsideofatry..catchblock.

RuleN25
Weshouldconsiderunsafenotincludingtheheaderfile<omp.h>inthefilewhere
OpenMPdirectivesareused.
Diagnosticmessagesbasedonthisrule:
V1006.Missingomp.hheaderfile.Use'#include<omp.h>'.

RuleN26
Weshouldconsiderunsafethepresenceofunusedvariablesmarkedinthe
reductiondirective.Thismaybeevidencebothofanerrorpresenceorsimplythat
someotherdirectiveorvariablewasforgottenandnotremovedduringtheprocess
ofcoderefactoring.
Exampleofnotusingabcdevariable:
#pragma omp parallel for reduction (+:sum, abcde)
for (i=1; i<999; i++)
{
sum = sum + a[i];
Diagnosticmessagesbasedonthisrule:
InthecurrentversionofVivaMP,thisruleisnotimplemented.

RuleN27
Youshouldconsiderunsafeanunprotectedaccessinaparallellooptoanarray
itemusinganindexdifferentfromthatusedforreading.
Note.Theerrormightoccurincaseofaprotectedaccessaswell(single,critical,
...),butforthepurposeofclearnesswewillconsiderherethattheprotectedaccess
issafeinthiscase.
Exceptions:
1.Theindexisaconstant.
Hereisanexampleofdangerouscode:
#pragma omp parallel for
for (int i=2; i < 10; i++)
array[i] = i * array[i-1]; //V1212
Thisisanexampleofsafecode:

#pragma omp parallel for


for (int i=2; i < 10; i++)
{
array[i] = array [i] / 2;
array_2[i] = i * array[i-1];
}
Diagnosticwarningsthataregeneratedrelyingonthisrule:
V1212.Dataracerisk.Whenaccessingthearray'%1%'inaparallelloop,different
indexesareusedforwritingandreading.

Conclusion
Ifyouareinterestedinmethodologyoftestingprogramcodeonthebasisofstatic
analysis,writeus(support@viva64.com).Wehopethatwewillfindmutualinterests
andopportunitiestocollaborate!

References
AndreyKarpov.Testingparallelprograms.http://www.viva64.com/en/a/0031/.
EvgeniyRyzhkov.VivaMPatoolforOpenMP.
http://www.viva64.com/en/a/0058/.

You might also like