अवास्तविक इंजन 4 का लंबे समय से प्रतीक्षित चेक

अवास्टविक इंजन 4 और पीवीएस-स्टूडियो

19 मार्च, 2014 अवास्तविक इंजन 4 सभी के लिए उपलब्ध हो गया। सदस्यता मूल्य केवल $ 19 प्रति माह है। स्रोत कोड भी github भंडार पर पोस्ट किए जाते हैं। इस क्षण से हमें मेल पर, ट्विटर पर और इस गेम इंजन की जांच करने के लिए बहुत सारे संदेश मिले। हम अपने पाठकों के अनुरोध को पूरा करते हैं। आइए देखें कि पीवीएस-स्टूडियो स्थिर कोड विश्लेषक का उपयोग करके स्रोत कोड में क्या दिलचस्प पाया जा सकता है।



अवास्तविक इंजन


अवास्तविक इंजन एक खेल इंजन है जिसे एपिक गेम्स द्वारा विकसित और रखरखाव किया जाता है। यह C ++ में लिखा गया है। आपको अधिकांश ऑपरेटिंग सिस्टम और प्लेटफॉर्म के लिए गेम बनाने की अनुमति देता है: माइक्रोसॉफ्ट विंडोज, लिनक्स, मैक ओएस और मैक ओएस एक्स, एक्सबॉक्स, एक्सबॉक्स 360, PlayStation 2, PlayStation पोर्टेबल, PlayStation 3, Wii, Dreamcast और Nintendo GameCube कंसोल।

आधिकारिक वेबसाइट: https://www.unrealengine.com/

विकिपीडिया: अवास्तविक इंजन पर विवरण।

एनएमके-आधारित परियोजना की जाँच के लिए विधि


अवास्तविक इंजन परियोजना सत्यापन इतना सरल नहीं है। इस परियोजना का परीक्षण करने के लिए, हमें एक नए अवसर का उपयोग करना था जिसे हमने हाल ही में पीवीएस-स्टूडियो स्टैंडअलोन में लागू किया है। इस वजह से, हमें लेख के प्रकाशन में थोड़ा विलंब करना पड़ा। हमने पीवीएस-स्टूडियो की रिलीज़ के बाद इसे प्रकाशित करने का फैसला किया, जहां यह नया फीचर पहले से ही होगा। कई लोग इसे आजमाना चाहते हैं। यह उन परियोजनाओं के सत्यापन की सुविधा देता है जहां जटिल या गैर-मानक विधानसभा प्रणाली का उपयोग किया जाता है।

पीवीएस-स्टूडियो का क्लासिक सिद्धांत इस प्रकार है:सूक्ष्मता यह है कि अवास्तविक इंजन 4 एक एनएमके-आधारित परियोजना है, इसलिए आप पीवीएस-स्टूडियो प्लगइन का उपयोग करके इसे सत्यापित नहीं कर सकते हैं।

मैं समझा दूंगा। विजुअल स्टूडियो पर्यावरण के लिए एक परियोजना है। लेकिन निर्माण एनएमके का उपयोग करके किया जाता है। इसका मतलब है कि प्लगइन को पता नहीं है कि कौन सी फाइलें और किस कुंजी के साथ संकलित किया गया है। तदनुसार, विश्लेषण संभव नहीं है। बल्कि, विश्लेषण संभव है, लेकिन प्रक्रिया समय लेने वाली है (प्रलेखन अनुभाग देखें: " विधानसभा स्वचालन प्रणाली में विश्लेषक का प्रत्यक्ष एकीकरण ")।

और यहाँ पीवीएस-स्टूडियो स्टैंडअलोन बचाव के लिए आता है! वह दो तरीकों से काम कर सकता है:
  1. आप किसी तरह प्रीप्रोसेस की गई फ़ाइलों को जेनरेट करते हैं, और वह उन्हें पहले से ही चेक कर लेगा।
  2. यह सभी आवश्यक सूचनाओं को संकलित करता है और "जासूसी" करता है।
अब हम दूसरे उपयोग के मामले में रुचि रखते हैं। यहाँ कैसे अवास्तविक इंजन परीक्षण चला गया:
  1. पीवीएस-स्टूडियो स्टैंडअलोन लॉन्च किया।
  2. "संकलक निगरानी" बटन पर क्लिक किया।
  3. "प्रारंभ निगरानी" बटन पर क्लिक किया। उन्होंने देखा कि संकलक निगरानी मोड चालू था।
  4. Visual Studio में Unreal इंजन प्रोजेक्ट खोला। हमने प्रोजेक्ट की असेंबली शुरू की। उसी समय, मॉनिटरिंग विंडो में आप देख सकते हैं कि कंपाइलर कॉल इंटरसेप्टेड हैं।
  5. जब विजुअल स्टूडियो में निर्माण समाप्त हो गया था, तो हमने स्टॉप मॉनिटरिंग बटन पर क्लिक किया। उसके बाद, पीवीएस-स्टूडियो विश्लेषक ने काम करना शुरू कर दिया।
नतीजतन, पीवीएस-स्टूडियो स्टैंडअलोन उपयोगिता विंडो में नैदानिक ​​संदेश दिखाई देते हैं।

संकेत। सुविधा के लिए, पीवीएस-स्टूडियो स्टैंडअलोन में निर्मित संपादक के बजाय विज़ुअल स्टूडियो का उपयोग करना बेहतर है। यह विश्लेषण के परिणामों को एक फ़ाइल में सहेजने के लिए पर्याप्त है, और फिर इसे विज़ुअल स्टूडियो वातावरण (मेनू-> पीवीएस-स्टूडियो-> ओपन / सेव-> ओपन विश्लेषण रिपोर्ट) से खोलें।

यह सब और बहुत कुछ लेख में वर्णित है " पीवीएस-स्टूडियो अब विंडोज और किसी भी कंपाइलर पर किसी भी बिल्ड सिस्टम का समर्थन करता है। आसान और आउट ऑफ द बॉक्सकृपया पीवीएस-स्टूडियो स्टैंडअलोन के साथ प्रयोग शुरू करने से पहले इस लेख को पढ़ना सुनिश्चित करें!

मान्यता परिणाम


अवास्तविक इंजन परियोजना कोड मुझे बहुत उच्च गुणवत्ता वाला लगा। उदाहरण के लिए, डेवलपर्स अपने विकास में स्थिर कोड विश्लेषण का उपयोग करते हैं। यह कोड में इस तरह के टुकड़े द्वारा इंगित किया गया है:
// Suppress static code analysis warning about a // potential comparison of two constants CA_SUPPRESS(6326); .... // Suppress static code analysis warnings about a // potentially ill-defined loop. BlendCount > 0 is valid. CA_SUPPRESS(6294) .... #if USING_CODE_ANALYSIS 

इन कोड अंशों को देखते हुए, हम Visual Studio में निर्मित स्थिर कोड विश्लेषक का उपयोग करते हैं। इस कोड विश्लेषक के बारे में: विजुअल स्टूडियो 2013 स्टेटिक कोड विश्लेषण गहराई में: क्या? कब और कैसे?

अन्य विश्लेषकों का उपयोग किया जा सकता है, लेकिन मुझे इसके बारे में कुछ भी पता नहीं है।

तो, कोड बहुत उच्च गुणवत्ता लिखा है। काम करते समय, स्थिर कोड विश्लेषण उपकरण का उपयोग किया जाता है। इसलिए, पीवीएस-स्टूडियो का उपयोग करके परियोजना की जांच करने पर, हमें कुछ संदिग्ध कोड टुकड़े मिले। हालांकि, किसी भी बड़ी परियोजना के साथ, इसमें त्रुटियां हैं। और उनमें से कुछ पीवीएस-स्टूडियो खोजने में सक्षम हैं। आइए देखें कि संदिग्ध को नोटिस करने के लिए क्या हुआ।

शुद्धिपत्र


 static bool PositionIsInside(....) { return Position.X >= Control.Center.X - BoxSize.X * 0.5f && Position.X <= Control.Center.X + BoxSize.X * 0.5f && Position.Y >= Control.Center.Y - BoxSize.Y * 0.5f && Position.Y >= Control.Center.Y - BoxSize.Y * 0.5f; } 

PVS-Studio चेतावनी: V501 समान उप-अभिव्यक्तियाँ हैं 'स्थिति.Y> = Control.Center.Y - BoxSize.Y * 0.5f' बाईं ओर और '&&' ऑपरेटर के दाईं ओर। svirtualjoystick.cpp 97

कृपया ध्यान दें कि चर "स्थिति। Y" की तुलना "Control.Center.Y - BoxSize.Y * 0.5%" के साथ दो बार की जाती है। यह स्पष्ट रूप से एक टाइपो है। अंतिम पंक्ति में, '-' ऑपरेटर को '+' ऑपरेटर से बदलें।

इस स्थिति में एक और समान टाइपो:
 void FOculusRiftHMD::PreRenderView_RenderThread( FSceneView& View) { .... if (View.StereoPass == eSSP_LEFT_EYE || View.StereoPass == eSSP_LEFT_EYE) .... } 

PVS-Studio चेतावनी: V501 समान उप-अभिव्यक्तियाँ हैं 'View.StereoPass == eSSP_LEFT_EYE' बाईं और दाईं ओर '' || ऑपरेटर। oculusrifthmd.cpp 1453

जाहिर है, ओकुलस रिफ्ट के साथ काम करना अभी तक बहुत परीक्षण नहीं किया गया है।

चलिए जारी रखते हैं।
 struct FMemoryAllocationStats_DEPRECATED { .... SIZE_T NotUsed5; SIZE_T NotUsed6; SIZE_T NotUsed7; SIZE_T NotUsed8; .... }; FMemoryAllocationStats_DEPRECATED() { .... NotUsed5 = 0; NotUsed6 = 0; NotUsed6 = 0; NotUsed8 = 0; .... } 

PVS-Studio चेतावनी: V519 'NotUsed6' चर को क्रमिक रूप से दो बार मान दिया गया है। शायद यह एक गलती है। जाँच लाइनें: 86, 88. memorybase.h 88

संरचना के सदस्यों को आरंभीकृत किया जाता है। एक टाइपो के कारण, 'NotUsed6' सदस्य को दो बार आरंभीकृत किया जाता है। और सदस्य 'NotUsed7' असिंचित रहे। हालाँकि, फ़ंक्शन नाम में प्रत्यय _DEPRECATED () कहता है कि यह पहले से ही निर्बाध कोड है।

कुछ और स्थान जहाँ एक चर को दो बार मान दिया जाता है:

अशक्त संकेत


बहुत बार मैं त्रुटि संचालकों में एक अशक्त सूचक को हटाने के लिए आता हूं। यह आश्चर्य की बात नहीं है। इस तरह के टुकड़े कठिन हैं और परीक्षण के लिए दिलचस्प नहीं हैं। आप अवास्तविक इंजन परियोजना में त्रुटि हैंडलर में अशक्त सूचक की dereferencing को पूरा कर सकते हैं:
 bool UEngine::CommitMapChange( FWorldContext &Context ) { .... LevelStreamingObject = Context.World()->StreamingLevels[j]; if (LevelStreamingObject != NULL) { .... } else { check(LevelStreamingObject); UE_LOG(LogStreaming, Log, TEXT("Unable to handle streaming object %s"), *LevelStreamingObject->GetName()); } .... } 

PVS-Studio चेतावनी: V522 अशक्त सूचक 'LevelStreamingObject' की डेरेफेरिंग हो सकती है। unrealengine.cpp 10768

यदि कोई त्रुटि होती है, तो मैं ऑब्जेक्ट का नाम प्रिंट करना चाहता हूं। बस यह वस्तु मौजूद नहीं है।

एक और टुकड़े पर विचार करें जहां अशक्त सूचक को बंद कर दिया जाएगा। यहां सब कुछ अधिक दिलचस्प है। शायद एक त्रुटि गलत मर्ज के कारण मौजूद है। किसी भी स्थिति में, टिप्पणी से पता चलता है कि कोड पूरा नहीं हुआ है:
 void FStreamingPause::Init() { .... if( GStreamingPauseBackground == NULL && GUseStreamingPause ) { // @todo UE4 merge andrew // GStreamingPauseBackground = new FFrontBufferTexture(....); GStreamingPauseBackground->InitRHI(); } } 

PVS-Studio चेतावनी: V522 अशक्त सूचक 'GStreamingPauseBackground' की जगह ले रहा है। स्ट्रीमिंगपॉडरेंडरिंग। सीपीसी 197

अशक्त बिंदुओं पर अधिक


लगभग किसी भी कार्यक्रम में, मैं V595 कोड ( उदाहरण ) के साथ एक टन चेतावनी देता हूं। ये चेतावनी निम्न स्थिति का संकेत देती है:

पॉइंटर को पहले डिरेल किया जाता है। इस सूचक के नीचे शून्य की समानता के लिए जाँच की जाती है। यह हमेशा एक गलती से दूर है। लेकिन यह एक बहुत ही संदिग्ध कोड है, और इसकी जाँच होनी चाहिए!

डायग्नोस्टिक्स V595 आपको इन ब्लंडर्स की पहचान करने की अनुमति देता है:
 /** * Global engine pointer. * Can be 0 so don't use without checking. */ ENGINE_API UEngine* GEngine = NULL; bool UEngine::LoadMap( FWorldContext& WorldContext, FURL URL, class UPendingNetGame* Pending, FString& Error ) { .... if (GEngine->GameViewport != NULL) { ClearDebugDisplayProperties(); } if( GEngine ) { GEngine->WorldDestroyed( WorldContext.World() ); } .... } 

PVS-Studio चेतावनी: V595 'GEngine' पॉइंटर का उपयोग करने से पहले इसे nullptr के खिलाफ सत्यापित किया गया था। लाइनों की जाँच करें: 9714, 9719

टिप्पणी पर ध्यान दें। वैश्विक चर GEngine शून्य हो सकता है। इसका उपयोग करने से पहले, आपको इसकी जांच करने की आवश्यकता है।

LoadMap () फ़ंक्शन में इस तरह का एक चेक होता है:
 if( GEngine ) 

केवल यहाँ दुर्भाग्य है। पॉइंटर का उपयोग किए जाने के बाद चेक स्थित है:
 if (GEngine->GameViewport != NULL) 

V595 चेतावनी काफी (82 टुकड़े)। मुझे लगता है कि उनमें से कई झूठे हो जाएंगे। इसलिए, मैं लेख को संदेशों के साथ नहीं दूंगा और उन्हें एक अलग सूची में दूंगा: ue-v595.txt

अतिरिक्त चर घोषणा


एक सुंदर गलती पर विचार करें। यह इस तथ्य के कारण है कि एक नया चर गलती से बनाया गया है, न कि पुराने का उपयोग किया जाता है।
 void FStreamableManager::AsyncLoadCallback(....) { .... FStreamable* Existing = StreamableItems.FindRef(TargetName); .... if (!Existing) { // hmm, maybe it was redirected by a consolidate TargetName = ResolveRedirects(TargetName); FStreamable* Existing = StreamableItems.FindRef(TargetName); } if (Existing && Existing->bAsyncLoadRequestOutstanding) .... } 

PVS-Studio चेतावनी: V561 यह 'एवेर्ज़िंग' वैरिएबल की वैल्यू असाइन करना बेहतर है, इसे नए सिरे से घोषित करें। पिछली घोषणा: streamablemanager.cpp, लाइन 325. streamablemanager.cpp 332

जैसा कि मैं इसे समझता हूं, वास्तव में, इसे इस तरह लिखा जाना चाहिए:
 // hmm, maybe it was redirected by a consolidate TargetName = ResolveRedirects(TargetName); Existing = StreamableItems.FindRef(TargetName); 

कार्यों को कॉल करते समय त्रुटियां


 bool FRecastQueryFilter::IsEqual( const INavigationQueryFilterInterface* Other) const { // @NOTE: not type safe, should be changed when // another filter type is introduced return FMemory::Memcmp(this, Other, sizeof(this)) == 0; } 

PVS-Studio चेतावनी: V579 मेम्कैंप फ़ंक्शन तर्क और इसके आकार को तर्क के रूप में प्राप्त करता है। यह संभवतः एक गलती है। तीसरे तर्क का निरीक्षण करें। pimplrecastnavmesh.cpp 172

टिप्पणी चेतावनी देती है कि मेम्कैंप () का उपयोग करना खतरनाक है। लेकिन, वास्तव में, यह प्रोग्रामर के विचार से भी बदतर है। तथ्य यह है कि एक फ़ंक्शन किसी ऑब्जेक्ट के केवल एक हिस्से की तुलना करता है।

आकार (यह) ऑपरेटर सूचक का आकार लौटाता है। अर्थात्, 32-बिट प्रोग्राम में, फ़ंक्शन पहले 4 बाइट्स की तुलना करता है। 64-बिट प्रोग्राम में, 8 बाइट्स की तुलना की जाएगी।

सही कोड है:
 return FMemory::Memcmp(this, Other, sizeof(*this)) == 0; 

Memcmp () फ़ंक्शन के साथ गलतफहमी वहाँ समाप्त नहीं होती है। निम्नलिखित कोड स्निपेट पर विचार करें:
 D3D11_STATE_CACHE_INLINE void GetBlendState( ID3D11BlendState** BlendState, float BlendFactor[4], uint32* SampleMask) { .... FMemory::Memcmp(BlendFactor, CurrentBlendFactor, sizeof(CurrentBlendFactor)); .... } 

PVS-Studio चेतावनी: V530 'मेम्कंप' फ़ंक्शन के रिटर्न मान का उपयोग किया जाना आवश्यक है। d3d11statecachepStreet.h 547

विश्लेषक यह देखकर आश्चर्यचकित था कि मेम्कंप () फ़ंक्शन का परिणाम किसी भी तरह से उपयोग नहीं किया गया है। और वास्तव में, यह एक गलती है। जैसा कि मैं इसे समझता हूं, वे यहां डेटा की तुलना नहीं करना चाहते थे। फिर आपको मेम्ची () फ़ंक्शन का उपयोग करने की आवश्यकता है:
 FMemory::Memcpy(BlendFactor, CurrentBlendFactor, sizeof(CurrentBlendFactor)); 

चर को खुद को सौंपा गया है।


 enum ECubeFace; ECubeFace CubeFace; friend FArchive& operator<<( FArchive& Ar,FResolveParams& ResolveParams) { .... if(Ar.IsLoading()) { ResolveParams.CubeFace = (ECubeFace)ResolveParams.CubeFace; } .... } 

PVS-Studio चेतावनी: V570 'ResolveParams.CubeFace' वेरिएबल को ही सौंपा गया है। rhi.h 1279

वैरिएबल 'ResolveParams.CubeFace' टाइप ECubeFace का है। ECubeFace प्रकार के चर का स्पष्ट उपयोग किया जाता है। यानी कुछ नहीं होता। फिर चर को खुद को सौंपा गया है। इस कोड में कुछ गड़बड़ है।

मिली त्रुटियों में सबसे सुंदर


जैसा

सबसे अधिक, मुझे यह त्रुटि पसंद आई:
 bool VertInfluencedByActiveBone( FParticleEmitterInstance* Owner, USkeletalMeshComponent* InSkelMeshComponent, int32 InVertexIndex, int32* OutBoneIndex = NULL); void UParticleModuleLocationSkelVertSurface::Spawn(....) { .... int32 BoneIndex1, BoneIndex2, BoneIndex3; BoneIndex1 = BoneIndex2 = BoneIndex3 = INDEX_NONE; if(!VertInfluencedByActiveBone( Owner, SourceComponent, VertIndex[0], &BoneIndex1) && !VertInfluencedByActiveBone( Owner, SourceComponent, VertIndex[1], &BoneIndex2) && !VertInfluencedByActiveBone( Owner, SourceComponent, VertIndex[2]) &BoneIndex3) { .... } 

PVS-Studio चेतावनी: V564 'और' ऑपरेटर को बूल प्रकार के मूल्य पर लागू किया जाता है। आप शायद कोष्ठकों को शामिल करना भूल गए हैं या '&&' ऑपरेटर का उपयोग करना चाहते हैं। particlemodules_location.cpp 2120

गलती को नोटिस करना आसान नहीं है। मुझे यकीन है कि आपने जल्दी से कोड के माध्यम से देखा और इसमें कुछ भी संदिग्ध नहीं देखा। विश्लेषक चेतावनी, दुर्भाग्य से, यह भी अजीब है और एक गलत सकारात्मक लगता है। इस बीच, हम एक बहुत ही दिलचस्प गलती से निपट रहे हैं।

चलिए इसका पता लगाते हैं। ध्यान दें कि VertInfluencedByActiveBone () फ़ंक्शन का अंतिम तर्क वैकल्पिक है।

कोड में, VertInfluencedByActiveBone () फ़ंक्शन को 3 बार कहा जाता है। उसके लिए दो बार 4 तर्क पारित किए जाते हैं। बाद के मामले में, केवल 3 तर्क हैं। यह गलती है।

भाग्य के लिए धन्यवाद, कोड संकलित करता है और त्रुटि अदृश्य होती है। यहाँ परिणाम है:
  1. 3 तर्कों के साथ एक फ़ंक्शन कहा जाता है: "वर्टीनफ्लुइटबायएक्टिवबोन (मालिक, स्रोतकंपोनेंट, वर्टाइंडेक्स [2])";
  2. ऑपरेटर '!' फ़ंक्शन के परिणाम पर लागू होता है;
  3. अभिव्यक्ति का परिणाम "; वर्टीनफ्लुइटबाइअक्टिवबोन (...)" प्रकार बूल का है;
  4. '&' ऑपरेटर (बिटवाइज़ और) परिणाम पर लागू होता है;
  5. सब कुछ सफलतापूर्वक संकलित होता है, क्योंकि 'और' ऑपरेटर के बाईं ओर टाइप बूल की अभिव्यक्ति है। '&' के दाईं ओर पूर्णांक चर BoneIndex3 है।
विश्लेषक को कुछ संदेह था जब उसने देखा कि & ऑपरेटर के तर्क में से एक 'बूल' था। उसने इसकी सूचना दी। और व्यर्थ नहीं।

त्रुटि को ठीक करने के लिए, एक अल्पविराम जोड़ें और समापन ब्रैकेट को दूसरी जगह रखें:
 if(!VertInfluencedByActiveBone( Owner, SourceComponent, VertIndex[0], &BoneIndex1) && !VertInfluencedByActiveBone( Owner, SourceComponent, VertIndex[1], &BoneIndex2) && !VertInfluencedByActiveBone( Owner, SourceComponent, VertIndex[2], &BoneIndex3)) 

ब्रेक स्टेटमेंट को भुला दिया जाता है


 static void VerifyUniformLayout(....) { .... switch(Member.GetBaseType()) { case UBMT_STRUCT: BaseTypeName = TEXT("struct"); case UBMT_BOOL: BaseTypeName = TEXT("bool"); break; case UBMT_INT32: BaseTypeName = TEXT("int"); break; case UBMT_UINT32: BaseTypeName = TEXT("uint"); break; case UBMT_FLOAT32: BaseTypeName = TEXT("float"); break; default: UE_LOG(LogShaders, Fatal, TEXT("Unrecognized uniform ......")); }; .... } 

PVS-Studio चेतावनी: V519 'BaseTypeName' चर को क्रमिक रूप से दो बार मान दिया गया है। शायद यह एक गलती है। जाँच लाइनें: 862, 863। openglshaders.cpp 863

बहुत शुरुआत में, "ब्रेक;" ऑपरेटर को भुला दिया जाता है। मुझे लगता है कि टिप्पणी और स्पष्टीकरण बहुत ही कम हैं।

Mikrooptimizatsii


पीवीएस-स्टूडियो विश्लेषक में नैदानिक ​​नियमों का एक छोटा समूह है जो कोड में छोटे अनुकूलन करने में मदद करता है। हालांकि, कभी-कभी वे बहुत उपयोगी होते हैं। एक उदाहरण के रूप में, असाइनमेंट ऑपरेटरों में से एक पर विचार करें:
 FVariant& operator=( const TArray<uint8> InArray ) { Type = EVariantTypes::ByteArray; Value = InArray; return *this; } 

चेतावनी PVS-Studio: V801 प्रदर्शन में कमी। संदर्भ के रूप में पहले फ़ंक्शन तर्क को फिर से परिभाषित करना बेहतर है। 'Const ... InArray' की जगह 'const ... & InArray' पर विचार करें। variant.h 198

किसी सरणी को मान से पास करना अच्छा विचार नहीं है। सरणी 'इनएरे' को निरंतर संदर्भ का उपयोग करके पास किया जा सकता है।

विश्लेषक माइक्रोओप्टिमाइज़ेशन से संबंधित काफी चेतावनी देता है। उनमें से सभी उपयोगी नहीं होंगे, लेकिन सिर्फ मामले में, मैं उन्हें सूचीबद्ध करूंगा: ue-v801-V803.txt

संदिग्ध राशि


 uint32 GetAllocatedSize() const { return UniformVectorExpressions.GetAllocatedSize() + UniformScalarExpressions.GetAllocatedSize() + Uniform2DTextureExpressions.GetAllocatedSize() + UniformCubeTextureExpressions.GetAllocatedSize() + ParameterCollections.GetAllocatedSize() + UniformBufferStruct ? (sizeof(FUniformBufferStruct) + UniformBufferStruct->GetMembers().GetAllocatedSize()) : 0; } 

PVS-Studio चेतावनी: V502 शायद '?:' ऑपरेटर अपेक्षा से भिन्न तरीके से काम करता है। '?:' ऑपरेटर की '+' ऑपरेटर की तुलना में कम प्राथमिकता है। सामग्रीधारी। 224

कोड बहुत जटिल है। यह स्पष्ट करने के लिए कि क्या गलत है, मैं एक सरल कृत्रिम उदाहरण बनाऊंगा:
 return A() + B() + C() + uniform ? UniformSize() : 0; 

एक निश्चित आकार की गणना की जाती है। चर 'वर्दी' के मूल्य के आधार पर, आपको 'यूनिफ़ॉर्माइज़ ()' या 0. जोड़ना चाहिए। वास्तव में, यह कोड उस तरह से काम नहीं करता है। अतिरिक्त ऑपरेटरों '+' की प्राथमिकता ऑपरेटर 'की प्राथमिकता से अधिक है ?:'।

यहाँ परिणाम है:
 return (A() + B() + C() + uniform) ? UniformSize() : 0; 

हम अवास्तविक इंजन कोड में एक समान स्थिति देखते हैं। यह मुझे लगता है कि प्रोग्रामर क्या चाहता है की गणना नहीं है।

एनम के साथ भ्रम


सबसे पहले, मैं इस मामले का वर्णन नहीं करना चाहता था, क्योंकि मुझे एक पर्याप्त बड़े कोड को लाने की आवश्यकता है। फिर भी मैंने आलस पर काबू पा लिया। कृपया धैर्य भी रखें।
 namespace EOnlineSharingReadCategory { enum Type { None = 0x00, Posts = 0x01, Friends = 0x02, Mailbox = 0x04, OnlineStatus = 0x08, ProfileInfo = 0x10, LocationInfo = 0x20, Default = ProfileInfo|LocationInfo, }; } namespace EOnlineSharingPublishingCategory { enum Type { None = 0x00, Posts = 0x01, Friends = 0x02, AccountAdmin = 0x04, Events = 0x08, Default = None, }; inline const TCHAR* ToString (EOnlineSharingReadCategory::Type CategoryType) { switch (CategoryType) { case None: { return TEXT("Category undefined"); } case Posts: { return TEXT("Posts"); } case Friends: { return TEXT("Friends"); } case AccountAdmin: { return TEXT("Account Admin"); } .... } } 

विश्लेषक यहां एक बार में कई V556 चेतावनी उत्पन्न करता है। तथ्य यह है कि 'स्विच ()' ऑपरेटर का तर्क EOnlineSharingReadCategory :: प्रकार का एक चर है। इस स्थिति में, 'केस' स्टेटमेंट किसी अन्य प्रकार EOnlineSharingPublishingCategory से मानों का उपयोग करते हैं :: प्रकार।

तार्किक त्रुटि


 const TCHAR* UStructProperty::ImportText_Internal(....) const { .... if (*Buffer == TCHAR('\"')) { while (*Buffer && *Buffer != TCHAR('\"') && *Buffer != TCHAR('\n') && *Buffer != TCHAR('\r')) { Buffer++; } if (*Buffer != TCHAR('\"')) .... } 

चेतावनी PVS-Studio: V637 दो विपरीत परिस्थितियों का सामना करना पड़ा। दूसरी शर्त हमेशा झूठी होती है। लाइनों की जाँच करें: 310, 312. उचित

यहां वे सब कुछ छोड़ना चाहते थे जो दोहरे उद्धरण चिह्नों में निहित है। एल्गोरिथ्म इस तरह होना चाहिए था:त्रुटि यह है कि पहले दोहरे उद्धरण को खोजने के बाद, हम पॉइंटर को अगले वर्ण पर नहीं ले जाते हैं। परिणामस्वरूप, दूसरी बोली तुरंत मिल जाती है। चक्र शुरू नहीं होता है।

मैं इसे सरल कोड में समझाऊंगा:
 if (*p == '\"') { while (*p && *p != '\"') p++; } 

त्रुटि को ठीक करने के लिए, आपको निम्नलिखित परिवर्तन करने होंगे:
 if (*p == '\"') { p++; while (*p && *p != '\"') p++; } 

संदेहास्पद पारी


 class FMallocBinned : public FMalloc { .... /* Used to mask off the bits that have been used to lookup the indirect table */ uint64 PoolMask; .... FMallocBinned(uint32 InPageSize, uint64 AddressLimit) { .... PoolMask = ( ( 1 << ( HashKeyShift - PoolBitShift ) ) - 1 ); .... } } 

PVS-Studio चेतावनी: V629 '1 << (HashKeyShift - PoolBitShift)' अभिव्यक्ति का निरीक्षण करने पर विचार करें। 64-बिट प्रकार के बाद के विस्तार के साथ 32-बिट मूल्य का बिट शिफ्टिंग। Mallocbinned.h 800

यहां कोई त्रुटि है या नहीं, इस पर निर्भर करता है कि 31 बिट में से 1 को शिफ्ट करना आवश्यक है या नहीं। चूंकि परिणाम 64-बिट पूलमास्क चर में रखा गया है, जाहिर है कि ऐसी आवश्यकता है।

अगर मैंने सही अनुमान लगाया है, तो लाइब्रेरी में मेमोरी आवंटन सबसिस्टम में त्रुटि है।

नंबर 1 टाइप इंट का है। इसका मतलब यह है कि 35 अंकों द्वारा 1 को स्थानांतरित करना असंभव है। औपचारिक रूप से, अस्पष्ट व्यवहार ( विवरण ) होगा। व्यवहार में, एक अतिप्रवाह होगा और गलत मान की गणना की जाएगी।

सही कोड है:
 PoolMask = ( ( 1ull << ( HashKeyShift - PoolBitShift ) ) - 1 ); 

पदावनत चेक


 void FOculusRiftHMD::Startup() { .... pSensorFusion = new SensorFusion(); if (!pSensorFusion) { UE_LOG(LogHMD, Warning, TEXT("Error creating Oculus sensor fusion.")); return; } .... } 

PVS-Studio चेतावनी: V668 null के खिलाफ 'pSensorFusion' पॉइंटर के परीक्षण में कोई समझदारी नहीं है, क्योंकि स्मृति को 'नए' ऑपरेटर का उपयोग करके आवंटित किया गया था। मेमोरी आवंटन त्रुटि के मामले में अपवाद उत्पन्न होगा। ऑक्युलसुथ्मैड.कंप 1594

लंबे समय तक, मेमोरी आवंटन त्रुटि के मामले में, 'नया' ऑपरेटर एक अपवाद फेंकता है। चेक "अगर ((PSensorFusion))" की जरूरत नहीं है।

बड़ी परियोजनाओं में, मुझे आमतौर पर ऐसी बहुत सी जगहें मिलती हैं। हैरानी की बात है कि, अवास्तविक इंजन में ऐसे कुछ स्थान हैं। सूची: ue-V668.txt

कॉपी पेस्ट करें


कॉपी-पेस्ट के उपयोग के कारण निम्नलिखित कोड के टुकड़े सबसे अधिक होने की संभावना है। शर्त के बावजूद, एक ही कार्रवाई की जाती है:
 FString FPaths::CreateTempFilename(....) { .... const int32 PathLen = FCString::Strlen( Path ); if( PathLen > 0 && Path[ PathLen - 1 ] != TEXT('/') ) { UniqueFilename = FString::Printf( TEXT("%s/%s%s%s"), Path, Prefix, *FGuid::NewGuid().ToString(), Extension ); } else { UniqueFilename = FString::Printf( TEXT("%s/%s%s%s"), Path, Prefix, *FGuid::NewGuid().ToString(), Extension ); } .... } 

PVS-Studio चेतावनी: V523 'तत्कालीन' कथन 'और' कथन के बराबर है। paths.cpp 703

इस तरह का एक और कोड:
 template< typename DefinitionType > FORCENOINLINE void Set(....) { .... if ( DefinitionPtr == NULL ) { WidgetStyleValues.Add( PropertyName, MakeShareable( new DefinitionType( InStyleDefintion ) ) ); } else { WidgetStyleValues.Add( PropertyName, MakeShareable( new DefinitionType( InStyleDefintion ) ) ); } } 

PVS-Studio चेतावनी: V523 'तत्कालीन' कथन 'और' कथन के बराबर है। slatestyle.h 289

अनेक वस्तुओं का संग्रह


एक अलग तिपहिया था। इसका वर्णन करना दिलचस्प नहीं है। मैं आपको केवल कोड स्निपेट और नैदानिक ​​संदेश दूंगा।
 void FNativeClassHeaderGenerator::ExportProperties(....) { .... int32 NumByteProperties = 0; .... if (bIsByteProperty) { NumByteProperties; } .... } 

PVS-Studio चेतावनी: V607 मालिकाना अभिव्यक्ति 'NumByteProperties'। codegenerator.cpp 633
 static void GetModuleVersion( .... ) { .... char* VersionInfo = new char[InfoSize]; .... delete VersionInfo; .... } 

PVS-Studio चेतावनी: V611 'नए टी []' ऑपरेटर का उपयोग करके मेमोरी आवंटित की गई थी, लेकिन 'डिलीट' ऑपरेटर का उपयोग करके जारी किया गया था। इस कोड का निरीक्षण करने पर विचार करें। शायद 'डिलीट [] वर्जनइन्फो?' का इस्तेमाल करना बेहतर होगा। windowsplatformexceptionhandling.cpp 107
 const FSlateBrush* FSlateGameResources::GetBrush( const FName PropertyName, ....) { .... ensureMsgf(BrushAsset, TEXT("Could not find resource '%s'"), PropertyName); .... } 

PVS-Studio चेतावनी: V510 'EnsureNotFalseFormatted' फ़ंक्शन को छठे वास्तविक तर्क के रूप में वर्ग-प्रकार चर प्राप्त करने की उम्मीद नहीं है। slategameresources.cpp 49

निष्कर्ष


Visual Studio में निर्मित एक स्थिर विश्लेषक का उपयोग करना उपयोगी है, लेकिन पर्याप्त नहीं है। हमारे पीवीएस-स्टूडियो विश्लेषक जैसे विशेष उपकरणों का उपयोग करना उचित है। उदाहरण के लिए, यदि आप PVS-Studio और VS2013 के स्थैतिक विश्लेषक की तुलना करते हैं, तो PVS-Studio 6 गुना अधिक त्रुटियां पाता है। आदेश में निराधार नहीं है - "तुलना पद्धति" । मैं हमारे कोड विश्लेषक की कोशिश करने के लिए गुणवत्ता कोड के सभी प्रेमियों को आमंत्रित करता हूं।

यह लेख अंग्रेजी में है।


यदि आप इस लेख को अंग्रेजी बोलने वाले दर्शकों के साथ साझा करना चाहते हैं, तो कृपया अनुवाद के लिंक का उपयोग करें: एंड्री कारपोव। अवास्तविक इंजन 4 की एक लंबी प्रतीक्षा की जांच

क्या आपने लेख पढ़ा है और एक प्रश्न है?
अक्सर हमारे लेखों से वही प्रश्न पूछे जाते हैं। हमने यहां उनके उत्तर एकत्र किए हैं: पीवीएस-स्टूडियो और कैप्पकैट, संस्करण 2014 के बारे में लेखों के पाठकों के सवालों के जवाब । कृपया सूची देखें।

Source: https://habr.com/ru/post/In219245/


All Articles