Unreal Engine開発プロセスの䞀郚ずしおの静的分析

PVS-Studioおよびアンリアル゚ンゞン






Unreal Engineプロゞェクトは開発䞭です-新しいコヌドが远加され、すでに蚘述されおいるものが倉曎されたす。 プロゞェクトの開発の避けられない結果は、できるだけ早く怜出するこずが望たしい新しい゚ラヌのコヌドに珟れるこずです。 ゚ラヌを枛らす1぀の方法は、PVS-Studio静的コヌドアナラむザヌを䜿甚するこずです。 さらに、アナラむザヌも急速に開発されおおり、新しい゚ラヌパタヌンを芋぀けるこずを孊んでいたす。そのいく぀かはこの蚘事で説明したす。 プロゞェクトのコヌドの品質に関心がある堎合は、この蚘事が圹立ちたす。



この出版物はAndrey Karpovによっお䜜成され、コヌド䟋はPVS-StudioチヌムのIlya IvanovずSergey Vasilievによっお提䟛されたした。 元の゜ヌスは、「Unreal Engine の開発プロセスの䞀郚ずしおの静的解析 」ずいうUnreal Engineブログです。



静的コヌド分析、理論



静的コヌド分析は、プログラムの゜ヌスコヌドの゚ラヌず欠点を識別するプロセスです。 静的分析は、自動化されたコヌドレビュヌプロセスず芋なすこずができたす。 コヌドレビュヌに぀いおもう少し詳しく芋おいきたしょう。



コヌドレビュヌは、最も叀く、最も有甚な欠陥怜出方法の1぀です。 ゜ヌスコヌドを泚意深く読み、それを改善するための掚奚事項を䜜成するこずから成りたす。 コヌドの読み取り䞭に、将来゚ラヌになる可胜性のある゚ラヌたたはコヌドのセクションが明らかになりたす。 たた、レビュヌ䞭のコヌドの䜜成者は、プログラムのこの郚分たたはその郚分がどのように機胜するかに぀いお説明すべきではないず考えられおいたす。 アルゎリズムは、プログラムのテキストずコメントから盎接理解する必芁がありたす。 この条件が満たされない堎合、コヌドを完成させる必芁がありたす。



原則ずしお、プログラマは他の誰かのコヌドの゚ラヌをはるかに簡単に認識するため、コヌドのレビュヌはうたく機胜したす。 コヌドレビュヌ手法の詳现に぀いおは、Steve McConnellの優れた曞籍「Perfect Code」Steve McConnell、「Code Complete」を参照しおください。



コヌドレビュヌの方法には、2぀の欠点がありたす。



  1. 非垞に高い䟡栌。 新しいコヌドを確認したり、掚奚事項を䜜成した埌にコヌドを再確認したりするために、メむンの䜜業から耇数のプログラマヌを定期的にそらす必芁がありたす。 この堎合、プログラマはリラックスするために定期的に䌑憩を取る必芁がありたす。 コヌドの倧きなフラグメントを䞀床に芋ようずするず、泚意がすぐに鈍くなり、コヌドをレビュヌする利点がすぐに消えおしたいたす。
  2. 人々は、新しい/倉曎されたコヌドに盎接関係しない゚ラヌを怜出するのが難しいず感じたす。 曞き蟌たれたばかりのフラグメントを考慮するず、 stdlib.hヘッダヌファむルが接続されおいないため、 malloc関数がその䞭で正しく動䜜しないず仮定するこずは困難です。 この状況に぀いおは、蚘事「 C蚀語の矎しい64ビット゚ラヌ 」で詳しく説明しおいたす。 別の䟋ヘッダヌファむル内の関数たたは倉数のタむプを倉曎したす。 理論的には、このような倉曎の埌、この関数たたは倉数が䜿甚されるすべおのコヌドを調べる必芁がありたす。 実際には、これは非垞に時間がかかり、原則ずしお、レビュヌはプログラマヌが䜕かを倉曎した堎所にのみ制限されたす。


䞀方では、定期的にコヌドをレビュヌしたいず思いたす。 䞀方、高すぎたす。 トレヌドオフは静的コヌド分析ツヌルです。 プログラムの゜ヌスコヌドを凊理し、コヌドの特定のセクションに泚目を集めるようにプログラマヌに掚奚したす。 同時に、アナラむザヌは疲れず、ヘッダヌファむルの線集の圱響を受けるすべおのコヌドをチェックしたす。 もちろん、プログラムは、プログラマヌのチヌムによっお実行されたコヌドの完党なレビュヌに代わるものではありたせん。 ただし、利益/䟡栌比により、静的分析の䜿甚は倚くの䌁業にずっお非垞に有甚なプラクティスになりたす。



他の゚ラヌ怜出方法論ず同様に、静的分析には長所ず短所がありたす。 プログラムをテストするための完璧な方法はありたせん。 最適な結果は、優れたコヌディングスタむル、静的コヌド分析、動的コヌド分析、単䜓テスト、回垰テストなど、さたざたなアプロヌチの組み合わせを䜿甚しお取埗できたす。



静的分析の重芁な利点は、コヌドに衚瀺された盎埌に倚くの゚ラヌを怜出できるこずです。぀たり、゚ラヌを修正するこずは非垞に安䟡です。



実際、゚ラヌが早期に怜出されるほど、修正のコストが䜎くなりたす。 したがっお、McConnellの著曞「Perfect Code」に蚘茉されおいるデヌタによるず、テスト段階での゚ラヌ修正は、コヌディングコヌドの蚘述段階での10倍の費甚がかかりたす。







è¡š1.怜出時間に応じた欠陥修正の平均コスト衚のデヌタは、S。McConnellquot; Perfect Codequot;の本から取られおいたす。










è¡š1.欠陥の怜出時間に応じた欠陥修正の平均コスト衚のデヌタはS. McConnellの「Perfect Code」による本から取られおいたす。



静的分析ツヌルを䜿甚するず、コヌドの䜜成段階に固有の倚数の゚ラヌを特定できるため、プロゞェクト党䜓の開発コストを倧幅に削枛できたす。



静的アナラむザヌを䜿甚するこずの重芁性は、時間ずずもに増倧したす。 これは、最新のアプリケヌションのコヌドサむズが埐々に増加しおいるためです。 プログラムはより倧きく、より難しくなっおいたす。 さらに、コヌドの゚ラヌ密床は、そのサむズに非線圢に䟝存したす。



プロゞェクトが倧きいほど、含たれるコヌドの1000行あたりの゚ラヌが倚くなりたす。 この興味深い衚をご芧ください。







è¡š2.プロゞェクトのサむズず䞀般的な゚ラヌ密床。デヌタ゜ヌスquot;プログラムの品質ずプログラマの生産性quot; Jones、1977、QUOT; Estimating Software CostsQUOT; Jones、1998。










è¡š2.プロゞェクトのサむズず䞀般的な゚ラヌ密床。 デヌタ゜ヌス「プログラムの品質ずプログラマの生産性」Jones、1977、「゜フトりェアコストの芋積もり」Jones、1998。



デヌタを認識しやすくするために、グラフを䜜成したす。







プロゞェクトの兞型的な゚ラヌ密床










チャヌト1.プロゞェクトの兞型的な゚ラヌ密床。 青は最倧量です。 赀は平均額です。 緑が䞀番少ない。



グラフから、プロゞェクトの成長に䌎い、プログラマはプロゞェクトの必芁な品質を維持できる手段をたすたす䜿甚する必芁に迫られおいるこずがわかりたす。 高品質のコヌドを䜜成するために同じこずを行うこずはできたせん。これは、たずえば8幎前に行われたした。 これはチヌムにずっお䞍快な発芋になる可胜性がありたす。圌らはい぀ものようにコヌドを曞いおいるようで、品質の状況は悪化しおいたす。



新しい方法論ずツヌルを習埗する必芁がありたす。さもなければ、プロゞェクトの成長に䌎い、叀い技術では十分ではなくなりたす。 そしお、䜿甚すべき非垞に有甚な手法の1぀は、調査した静的コヌド分析です。



読者が静的コヌド分析の方法論にただ粟通しおいない堎合は、なんずか興味を持っおいただければ幞いです。 圌女ずのより詳现な知り合いのために、いく぀かのリンクを提䟛したす



  1. ゞョン・カヌマック。 静的コヌド分析 。
  2. りィキペディア 静的コヌド分析 。
  3. りィキペディア 静的コヌド分析のためのツヌルのリスト 。
  4. アル・ベッシヌ、ケン・ブロック、ベン・シェルフ、アンディ・チョり、ブラむアン・フルトン、セス・ハレム、チャヌルズ・アンリ・グロス、アシャ・カムスキヌ、スコット・マクピヌク、ドヌ゜ン・゚ングラヌ。 数十億行のコヌドを埌で静的分析を䜿甚しお実䞖界のバグを芋぀けたす。
  5. ゚カテリヌナ・ミロビドバ。 静的コヌド分析に関するビデオの遞択 。
  6. PVS-Studioチヌムのブログ 。


理論から実践ぞず移行し、静的解析がアンリアル゚ンゞンゲヌム゚ンゞンのようなプロゞェクトにどのように圹立぀かを芋おみたしょう。



アンリアル゚ンゞン



私たちのチヌムは、Unreal Engineコヌドで䜜業できるこずを再び光栄に思いたす



すでに2幎前にこれを行いたしたが、過去数幎間、コヌドの線集ず改善に関する䜜業が蓄積されおきたした。 2幎間の䌑憩の埌、そのようなプロゞェクトのコヌドベヌスを芋るず、垞に䟿利で興味深いものです。 これにはいく぀かの理由がありたす。



たず第䞀に、アナラむザヌの誀怜知を調べるこずに興味がありたす。 ツヌルで䜕かを修正できたす。これにより、䞍芁なメッセヌゞの数が枛りたす。 誀怜知ずの闘いは、コヌドアナラむザヌの開発者にずっお継続的なタスクです。 このトピックに興味がある人は、蚘事「 静的アナラむザヌが停陜性ず戊う方法ず理由 」を読むこずを提案したす。



2幎間、Unreal Engineのコヌドベヌスは倧きく倉化したした。 いく぀かの断片が消え、いく぀かが远加されたした。 堎合によっおはフォルダ党䜓も。 したがっお、コヌドのすべおの郚分に十分な泚意が払われたわけではありたせん。぀たり、PVS-Studioにも䜜業がありたす。



コヌドの品質に倚倧な泚意を払い、PVS-Studioなどのツヌルを䜿甚しおいるEpic Gamesを称賛したいず思いたす。 もちろん、読者はにやにや笑うこずができたす。「もちろん、あなたのチヌムはEpic Gamesを称賛すべきです。それはあなたのクラむアントだからです。」 Epic Gamesの開発者に関する肯定的なフィヌドバックを残す動機がありたす。 しかし、私は䌚瀟を心から賞賛したした。 静的コヌド分析ツヌルが䜿甚されおいるずいう事実は、プロゞェクト開発サむクルの成熟床ず、コヌドの信頌性ずセキュリティに察する䌚瀟の懞念を瀺しおいたす。



PVS-Studioを䜿甚するずコヌドの品質が倧幅に向䞊するず確信しおいるのはなぜですか 次のようなプロゞェクトでも゚ラヌを簡単に怜出できる最も匷力な静的アナラむザヌの1぀であるためです。





PVS-Studioを䜿甚するず、コヌドが1ステップ远加されたす。 Epic Gamesは、プロゞェクトでUnreal Engineを䜿甚するすべおの人の面倒を芋おくれたす。 修正されたミスごずに、誰かの頭痛が軜枛されたす。



興味深いバグ



Unreal Engineで発芋しお修正したすべおの゚ラヌに぀いおは説明したせんが、泚目に倀するいく぀かの゚ラヌのみを匷調したす。 興味のある方は、GitHubのプルリク゚ストを芋お、他の゚ラヌに慣れるこずができたす。 ゜ヌスコヌドず指定されたプルリク゚ストにアクセスするには、GitHubのUnreal Engineリポゞトリにアクセスする必芁がありたす。 これを行うには、GitHubアカりントずEpicGamesアカりントが必芁です 。これらのアカりントはunrealengine.comにリンクする必芁がありたす。 その埌、GitHubのEpic Gamesコミュニティに参加するための招埅を受け入れる必芁がありたす。 指瀺



PVS-Studioアナラむザヌの開発は、新しい蚺断の出珟だけでなく、既存の蚺断の改善にも関連しおいたす。 たずえば、倉数の可胜な倀を蚈算するアルゎリズムが改善されおいたす。 これにより、玄1幎前に、アナラむザヌはこの皮の゚ラヌを怜出し始めたした。



uint8* Data = (uint8*)PointerVal;

if (Data != nullptr || DataLen == 0)
{
  NUTDebug::LogHexDump(Data, DataLen);
}
else if (Data == nullptr)
{
  Ar.Logf(TEXT("Invalid Data parameter."));
}
else // if (DataLen == 0)
{
  Ar.Logf(TEXT("Invalid DataLen parameter."));
}
      
      





PVS-Studio: V547 Expression 'Data == nullptr' is always true. unittestmanager.cpp 1924



(Data != nullptr || DataLen == 0) , , Data nullptr. , (Data == nullptr) .



:



if (Data != nullptr && DataLen > 0)
      
      





V547 2010 . , . DataLen, , . , , .



PVS-Studio, . , «», .. .



«», , C++. C++11, C++14 . , . V714, range-based . Unreal Engine V714 :



for (TSharedPtr<SWidget> SlateWidget : SlateWidgets)
{
  SlateWidget = nullptr; 
}
      
      





PVS-Studio: V714 Variable is not passed into foreach loop by a reference, but its value is changed inside of the loop. vreditorradialfloatingui.cpp 170



nullptr SlateWidgets. , SlateWidget — , . . , :



for (TSharedPtr<SWidget> &SlateWidget : SlateWidgets)
{
  SlateWidget = nullptr; 
}
      
      





, , , . , V767 2015 , Unreal Engine. PVS-Studio 6.07 (8 2016). :



for(int i = 0; i < SelectedObjects.Num(); ++i)
{
  UObject* Obj = SelectedObjects[0].Get();
  EdObj = Cast<UEditorSkeletonNotifyObj>(Obj);
  if(EdObj)
  {
    break;
  }
}
      
      





PVS-Studio: V767 Suspicious access to element of 'SelectedObjects' array by a constant index inside a loop. skeletonnotifydetails.cpp 38



, UEditorSkeletonNotifyObj. - i 0.



:



UObject* Obj = SelectedObjects[i].Get();
      
      





V763, , V767, PVS-Studio 6.07. , , , RunTest:



bool FCreateBPTemplateProjectAutomationTests::RunTest(
  const FString& Parameters)
{
  TSharedPtr<SNewProjectWizard> NewProjectWizard;
  NewProjectWizard = SNew(SNewProjectWizard);

  TMap<FName, TArray<TSharedPtr<FTemplateItem>> >& Templates =
    NewProjectWizard->FindTemplateProjects();
  int32 OutMatchedProjectsDesk = 0;
  int32 OutCreatedProjectsDesk = 0;
  GameProjectAutomationUtils::CreateProjectSet(Templates, 
    EHardwareClass::Desktop, 
    EGraphicsPreset::Maximum, 
    EContentSourceCategory::BlueprintFeature,
    false,
    OutMatchedProjectsDesk,
    OutCreatedProjectsDesk);

  int32 OutMatchedProjectsMob = 0;
  int32 OutCreatedProjectsMob = 0;
  GameProjectAutomationUtils::CreateProjectSet(Templates, 
    EHardwareClass::Mobile,
    EGraphicsPreset::Maximum,
    EContentSourceCategory::BlueprintFeature,
    false,
    OutMatchedProjectsMob,
    OutCreatedProjectsMob);

  return ( OutMatchedProjectsDesk == OutCreatedProjectsDesk ) &&
         ( OutMatchedProjectsMob  == OutCreatedProjectsMob  );
}
      
      





:





, :



return ( OutMatchedProjectsDesk == OutCreatedProjectsDesk ) &&
       ( OutMatchedProjectsMob  == OutCreatedProjectsMob  );
      
      





, . , , CreateProjectSet , , .



CreateProjectSet:



static void CreateProjectSet(.... int32 OutCreatedProjects,
                                  int32 OutMatchedProjects)
{
  ....
  OutCreatedProjects = 0;
  OutMatchedProjects = 0;
  ....
  OutMatchedProjects++;
  ....
  OutCreatedProjects++;
  ....
}
      
      





PVS-Studio :





, , OutCreatedProjects OutMatchedProjects , 0.



: . :



static void CreateProjectSet(.... int32 &OutCreatedProjects,
                                  int32 &OutMatchedProjects)
      
      





, - . . , break:



{
  case EWidgetBlendMode::Opaque:
    ActualBackgroundColor.A = 1.0f;
  case EWidgetBlendMode::Masked:
    ActualBackgroundColor.A = 0.0f;
}
      
      





:



checkf(GPixelFormats[PixelFormat].BlockSizeX 
    == GPixelFormats[PixelFormat].BlockSizeY 
    == GPixelFormats[PixelFormat].BlockSizeZ 
    == 1, 
  TEXT("Tried to use compressed format?"));
      
      





- C++ , , V709.



, PVS-Studio. , ?



, . .



{
  case EWidgetBlendMode::Opaque:
    ActualBackgroundColor.A = 1.0f;
  case EWidgetBlendMode::Masked:
    ActualBackgroundColor.A = 0.0f;
}
      
      





. , , .



: - ?





PVS-Studio , , , . , PVS-Studio. .



, , , , , .



, , , , .



, . , , , . , , , .



, . , , , — .



. " ' '" (en). :



( , , )?



, 90% « ».



, . 100 50 50 — . . , , , , « ».



, . — . «/ » . , — .



, .



. .



. , , . , , . , //. : " — ".



: . , :





. - , - . — - , . .





Unreal Engine , PVS-Studio, , .



PVS-Studio . PVS-Studio , , ..



.



All Articles