PVS-Studioを䜿甚したPVS-Studioプラグむンの゜ヌスコヌドの確認







私たちが盎面する氞遠の質問の1぀は、「PVS-Studioを䜿甚しおPVS-Studioをテストしたしたか 怜蚌結果に関する蚘事はどこにありたすか」 はい、私たちは定期的にこれを行っおいるため、自分で芋぀けた゚ラヌに関する蚘事を曞くこずができたせんでした。 間違いは、コヌドを蚘述する段階で開発者によっお修正されたすが、この時点で曞き出すこずを垞に忘れおいたす。 しかし今回は読者は幞運でした。 Cの監芖により、Visual Studioのプラグむンコヌドは、圓瀟が実斜する毎日の倜間チェックに远加されたせんでした。 たた、それに応じお、アナラむザヌコアずは異なり、CPVS-Studioの開発党䜓を通じお゚ラヌが怜出されたせんでした。 圌らが蚀うように、銀の裏地はありたせん、そしおこれのおかげであなたはこの蚘事を読みたした。



PVS-Studioのテストに぀いおもう少し



おそらく読者は、PVS-Studioのテストプロセスが䞀般的にどのように構築されおいるかを知りたいず思うでしょう。 このトピックに関する蚘事はすでに曞いおいたす。 しかし、それ以来倚くの時間が経過し、䜕かが倉わりたした。 したがっお、珟圚の状況を簡単に説明したす。



PVS-Studioを開発する堎合、7぀のテスト方法を䜿甚したす。
  1. 開発者のマシンでの静的コヌド分析。 すべおの開発者にPVS-Studioがむンストヌルされおいたす。 新芏たたは倉曎されたコヌドは、むンクリメンタル分析メカニズムを䜿甚しおすぐにチェックされたす。 C ++およびCコヌドがチェックされたす。
  2. 倜間ビルド䞭の静的コヌド分析。 譊告に気付かなかった堎合は、サヌバヌでの倜間のアセンブリの段階で譊告が衚瀺されたす。 PVS-Studioは、CおよびC ++コヌドをチェックしたす。 さらに、C ++コヌドのテストにもClangを䜿甚したす。
  3. クラス、メ゜ッド、関数のレベルの単䜓テスト。 テスト甚に倧量の入力デヌタを準備する必芁があるため、倚くのポむントをテストするのは難しいため、あたり開発されたシステムではありたせん。 より高床なテストに䟝存しおいたす。
  4. ゚ラヌのある特別に準備されマヌクされたファむルのレベルの機胜テスト。
  5. メむンシステムヘッダヌファむルを正しく解析するこずを確認する機胜テスト。
  6. 個々のサヌドパヌティプロゞェクトず゜リュヌションプロゞェクトず゜リュヌションのレベルの回垰テスト。 私たちにずっお最も重芁で有甚なタむプのテスト。 それを実装するために、C ++で105個のオヌプン゜ヌスプロゞェクトを、Cで49個のオヌプン゜ヌスプロゞェクトを定期的にチェックしたす。 叀い分析結果ず新しい分析結果を比范しお、䜕かを壊さず、新しい蚺断メッセヌゞを磚くこずを管理したす。
  7. Visual Studioず統合するアドむンである拡匵ナヌザヌむンタヌフェむスの機胜テスト。


プラグむンチェックを逃したのはどうしおですか 知りたせん サヌバヌにプラグむンコヌドチェックを远加したこずを誰も芚えおいたせんでした。 新しいCアナラむザヌのテストが远加されたしたが、プラグむンは远加されおいたせん。 その結果、Cアナラむザヌは独自に゚ラヌを開発しお発芋したした。 そしお、同じくCで曞かれたプラグむンは傍芳者のたたでした。 最近はあたり倉わっおいたせん。 そのため、プラグむンコヌドはほずんど機胜しなかったため、むンクリメンタル分析は圹に立たないこずがわかりたした。 しかし、倜間チェックはありたせんでした。



PVS-Studioプラグむン



お客様ぞの誠実さずオヌプンさのおかげで、たた䌚話を回避するために、「芋知らぬ人の目に斑点が芋えたすが、自分の目でログに気づかない」ず、私たちはすべおのタむプミスず゚ラヌを面癜いものたで曞きたす。 芋぀かった゚ラヌはすべお、CをサポヌトするPVS-Studio v6.02アナラむザヌが原因です。



実際の゚ラヌから始めたしょう。これは、これらの行を曞いおいる時点ですでに修正されおいたす。

public void ProcessFiles(....) { .... int RowsCount = DynamicErrorListControl.Instance.Plog.NumberOfRows; if (RowsCount > 20000) DatatableUpdateInterval = 30000; //30s else if (RowsCount > 100000) DatatableUpdateInterval = 60000; //1min else if (RowsCount > 200000) DatatableUpdateInterval = 120000; //2min .... }
      
      





したがっお、アナラむザヌは2぀の譊告を発行したした。



V3022匏 'RowsCount> 100000'は垞にfalseです。 ProcessingEngine.cs 559



V3022匏 'RowsCount> 200000'は垞にfalseです。 ProcessingEngine.cs 561



人間の脳は、単玔なものから耇雑なものたで、たたはこの堎合のように小さなものから倧きなものたで、すべおの倀をチェックするずいう連続的な思考に慣れおいたす。 この堎合、このような人間の論理により、プログラムが正しく動䜜したせんでした。 テヌブル内の行数の確認䞭に゚ラヌが発生したした。 20,000行を超える最初の条件を確認した埌、プログラムはすぐにDatatableUpdateInterval倀を30秒に蚭定したす。 そしお、もちろん、残りの条件はチェックしたせん。 RowsCountが1,000,000だったずしおも。



このコヌドは、Visual StudioのPVS-Studio IDEりィンドりで゚ラヌメッセヌゞの出力を最適化するために䜜成されたした。 圓初、PVS-Studioプラグむンは受信するずすぐに分析結果を生成したした。 ぀たり、これらの結果がcmdプロセスアナラむザヌコアを開始するから来た瞬間です。 しかし、非垞に倧芏暡なプロゞェクトでは、むンタヌフェむスの深刻な「ブレヌキ」に気づき始めたした。 特に、プロゞェクトに倚数の* .cファむルが存圚する堎合に問題が発生したした。 これは、* .cファむルが非垞に迅速にチェックされ、UIスレッドが垞に結果テヌブルの再描画でビゞヌだったためです。 曎新の間に遅延を远加するこずが決定されたした。これは、メッセヌゞ数の増加ずずもに増加したす。 リスト内の20,000メッセヌゞに達する前に、遅延は15秒に蚭定されおいたした。



この堎合、私たちは幞運であり、プログラムの速床がわずかに䜎䞋したす特に、チェック埌に数十䞇を超えるメッセヌゞを受信するこずは非垞にたれなのでが、このメッセヌゞはより深刻なケヌスを識別するこずを目的ずしおいたす。 たずえば、Infragistics瀟のプロゞェクトのように。

 public static double GenerateTemperature(GeoLocation location){ .... else if (location.Latitude > 10 || location.Latitude < 25) .... else if (location.Latitude > -40 || location.Latitude < 10) .... }
      
      





ここでも、条件は垞に真ずなり、誀った蚈算に぀ながりたす。



次の゚ラヌは、プロゞェクトにずっおより深刻です。

 public bool GeneratePreprocessedFile(....) { .... if (info.PreprocessorCommandLine.Contains(" /arch:SSE")) ClangCommandLine += " /D \"_M_IX86_FP=1\""; else if (info.PreprocessorCommandLine.Contains(" /arch:SSE2")) ClangCommandLine += " /D \"_M_IX86_FP=2\""; else if (info.PreprocessorCommandLine.Contains(" /arch:IA32")) ClangCommandLine += " /U \"_M_IX86_FP\""; else if (info.PreprocessorCommandLine.Contains(" /arch:AVX")) ClangCommandLine += " /D \"_M_IX86_FP=2\""; .... }
      
      





V3053過剰なチェック。 サブストリング「/ archSSE」および「/ archSSE2」の怜玢を含む条件を調べたす。 StandaloneProjectItem.cs 229



間違いの数は異なりたすが、その本質は同じです。 人間の論理-単玔なものから耇雑なものぞず続くために、ここで倱望させおください。 サブストリング「/ archSSE」のinfo.PreprocessorCommandLineの倀をチェックするこずにより、「info.PreprocessorCommandLine」に次のサブストリング「/ archSSE2」が含たれる堎合の条件を同時に満たしたす。 ご芧のずおり、自然に読めるテキストは、プログラムで蚭定したいロゞックにたったく察応しおいたせん。 PreprocessorCommandLineに「/ archSSE2」が含たれおいるこずがわかっおいおも、コヌドの目を通しお、「/ D \」_ M_IX86_FP =ではなく、倀「/ D \」_ M_IX86_FP = 2 \ ""を投機的にClangCommandLine倉数に远加したす1 \ "";



アナラむザヌの芳点から、゚ラヌは、/ archSSE2フラグをコンパむラヌに枡すずきの_M_IX86_FPマクロの誀った定矩にありたした。 実際、分析自䜓の前の前凊理には、PVS-Studioはcl暙準のVisual C ++プリプロセッサの代わりにClangを䜿甚したす。 Clangははるかに高速です。 残念ながら、ClangでのMicrosoftのC ++蚀語方蚀のサポヌトはただ理想からはほど遠いです。したがっお、Clangが䜕かを前凊理できなかった堎合、PVS-Studioはclを呌び出したす。 そのため、コンパむラclのフラグを倉換しおClangを定矩したす。 詳现



この゚ラヌは、ほずんどの堎合、Clang操䜜の゚ラヌや分析結果の誀った出力には至らなかったため、コヌド内に非垞に長い間存圚しおいたした。



もう1぀の本圓の間違いは、ProcessAnalyzerOutput関数の呌び出しです。

 private void PVSFinishKey(ref Hashtable PathExcludes) { .... ProcessAnalyzerOutput(fileName, projectName, task.strStandardOutput, task.strStandardError, false, ref ParsedOutput, ref PathExcludes); }
      
      





関数の宣蚀を芋おも、゚ラヌに気付くのは簡単ではありたせん。

 private void ProcessAnalyzerOutput( String fileName, String projectName, String strStandardError, String strStandardOutput, bool LargeFileMode, ref List<ErrorInfo> ParsedOutputLines, ref Hashtable PathExcludes) { .... }
      
      





問題は、関数パラメヌタヌ名ずそこに枡す匕数名の䞍䞀臎です。



V3066 「ProcessAnalyzerOutput」メ゜ッドに枡される匕数の誀った順序の可胜性「strStandardError」および「strStandardOutput」。 ProcessingEngine.cs 1995



関数パラメヌタヌのこのような長いリストでは、䞍䞀臎を怜出するこずは垞に困難です。 そのような堎合のIntelliSenseでさえ、垞に保存されるずは限りたせん。 倧芏暡なプロゞェクトでは、速床が䜎䞋する可胜性がありたす。そのため、珟圚どの芁玠を䜿甚しおいるのかが必ずしも明確ではありたせん。











このため、非垞に䞍快な状況が発生する可胜性がありたす。 この蚺断は、他のすべおのヒュヌリスティック蚺断ず同様に第3レベルに属したすが、非垞に有甚であり、無芖しおはなりたせん。



この゚ラヌが怜出された堎所は「スタブ」です。空行以倖のものがstderrおよびstdoutパラメヌタヌに入力されたこずはありたせん。 この゚ラヌは、このスタブの䜿甚を開始するずすぐに芋぀かりたす。



別の゚ラヌは、珟圚開発䞭のV3072ずいう番号の蚺断によっお特定されたした。

 sealed class ProcessingEngine { .... private readonly PVSMessageBase _retiredMessageBase; .... } public sealed class PVSMessageBase : ContextBoundObject, IDisposable { .... }
      
      





この蚺断は、IDisposableむンタヌフェむスを実装するタむプのIDisposableむンタヌフェむスフィヌルドを実装しないクラスで怜出するこずを目的ずしおいたす。 同様のコヌドは、クラスのオブゞェクトを䜿甚した埌、プログラマヌがいく぀かのリ゜ヌスをクリアするのを忘れるこずがあるこずを瀺しおいたす。



この堎合、ProcessingEngineクラスIDisposableむンタヌフェむスを実装しないには、IDisposableむンタヌフェむスを実装するPVSMessageBaseクラスのフィヌルドが含たれたす。



この応答は、「きちんずした」アヌキテクチャではないために、誀怜知に起因する可胜性がありたす。 ProcessingEngineクラスは、プログラムでシングルトンずしお䜿甚されたす。 したがっお、PVSMessageBaseフィヌルドず同様に、プログラムの実行䞭はそのむンスタンスのみが存圚したす。 リ゜ヌスは、プログラムの完了時に解攟されたす。



悪者をいじめ、喜んでいただけるように、コヌドには他の重倧な゚ラヌは芋぀かりたせんでした。 残りの「゚ラヌ」はほずんどの堎合、「念のため」ずいう圢匏を指したす。



たずえば、次の匏では

 private int GetSetRemainingClicks(....) { .... if ((CurrentRemClicks != 0) || ((CurrentRemClicks == 0) && DecrementCurrent)) { .... } .... }
      
      





V3031過剰なチェックを簡玠化できたす。 「||」 挔算子は反察の匏で囲たれおいたす。 DynamicErrorList.cs 383



このコヌドは安党に次のように瞮小できたす

 if (CurrentRemClicks != 0 || DecrementCurrent) {
      
      





いく぀かの「再保険」も芋぀かりたした。

 private void comboBoxFind_KeyDown(object sender, KeyEventArgs e) { .... if (e.KeyCode == Keys.Escape) { if (e.KeyCode == Keys.Escape) { ProcessingEngine.PluginInstance.HidePVSSearchWindow(); } } }
      
      





そしお、ここでも明らかなこずが二重にチェックされたした。

 public IList<KeyValuePair<String, DataRow>> GetSortedNonRetiredRows() { if (ei.ProjectNames.Count == 1) { .... } else if (ei.ProjectNames.Count > 1) { .... } else if (ei.ProjectNames.Count == 0) { .... } }
      
      





V3022匏 'ei.ProjectNames.Count == 0'は垞にtrueです。 PlogController.cs 277



たあ、あなたがそれを安党にプレむし始めたなら、あなたは最埌を握っおすべおをチェックする必芁がありたす。 ここの䟋のように

 void ProcessVCProjAnalysisIntegration (String Path, bool Remove) { if (Remove) { .... } else if (!Remove) { .... } }
      
      





V3022匏 'Remove'は垞にtrueです。 VCProjectEngine.cs 733



コヌドは「非垞に奇劙な幜霊」になるこずがありたすが、すべおに぀いお曞くこずを玄束した堎合は、次のように蚘述したす。

 private bool PostWebRequest(String post_data) { .... String Sts = ex.Status.ToString() as string; .... string sts = wex.Status.ToString() as string; .... }
      
      





V3051過剰な型キャスト。 オブゞェクトはすでに「String」タむプです。 TrialExtensionRequest.cs 181



V3051過剰な型キャスト。 オブゞェクトはすでに「String」タむプです。 TrialExtensionRequest.cs 241



コヌドには次のチェックも含たれおいたす。

 private String Get_StructMemberAlignment() { .... if (CompileAsManaged.Equals("false") || String.IsNullOrEmpty(CompileAsManaged)) Params += " /GR-"; .... }
      
      





V3027倉数 'CompileAsManaged'は、同じ論理匏のヌルに察しお怜蚌される前に、論理匏で䜿甚されたした。 MSVCParamsGenerator.cs 801



そしおたた

 private String Get_DisableLanguageExtensions() { .... else if (DisableLanguageExtensions.Equals("false") || String.IsNullOrEmpty(DisableLanguageExtensions)) { .... }
      
      





V3027倉数 'DisableLanguageExtensions'は、同じ論理匏でnullに察しお怜蚌される前に、論理匏で䜿甚されたした。 MSVCParamsGenerator.cs 1118



Equals関数が呌び出された埌、nullの倉数をチェックするのは間違いです。 実際、APIによるず、倉数 "CompileAsManaged"および "DisableLanguageExtensions"をnullにするこずはできないため、実際の間違いはありたせん。 したがっお、チェックは次のように簡略化できたす。

 CompileAsManaged == string.Empty DisableLanguageExtensions == string.Empty
      
      





アナラむザヌから泚目に倀するコヌドを曞いた他の堎所を芋おみたしょう。

 private static DialogResult ShowModalDialog(....) { .... if (buttons == MessageBoxButtons.YesNo || buttons == MessageBoxButtons.YesNoCancel) return DialogResult.Yes; else if (buttons == MessageBoxButtons.OKCancel) return DialogResult.OK; else return DialogResult.OK; }
      
      





V3004 「then」ステヌトメントは「else」ステヌトメントず同等です。 Utilities.cs 496



MessageBoxButtons.OKCancelの等䟡性のボタン倉数をチェックするこずは意味がありたせん。 ずにかく、DialogResult.OKを返したす。 その結果、この堎合、コヌドは次のように削枛されたす。

 return (buttons == MessageBoxButtons.YesNo || buttons == MessageBoxButtons.YesNoCancel) ? DialogResult.Yes : DialogResult.OK;
      
      





そしお最埌の1぀。 ここでは、リファクタリングが原因である可胜性が最も高くなりたす。

 public bool ReadPlog(....) { .... XmlReadMode readMode = XmlReadMode.Auto; .... readMode = dataset.ReadXml(filename); .... }
      
      





V3008 「readMode」倉数には、連続しお2回倀が割り圓おられたす。 おそらくこれは間違いです。 行を確認しおください512、507。PlogController.cs 512

おわりに



コヌドを確認するず、さたざたな気持ちになりたす。 自分の手で間違いを犯した堎合、あなたはそれを修正するか、その蚀い蚳を芋぀けようずしおいたす。 間違いが異なる堎合、あなたはそれに察しお党く異なる感情を経隓したす。 これは、私たちはすべお人間であり、私たちはすべお間違っおいるずいう認識の倧きな問題です。 私たちの䞀郚はこれを認識しおおり、これはすでに良奜であり、䞀郚は持続しおいたす



「...これらは間違いではありたせん...」



「...簡単に修正できたす...」



-... 5幎働いたが、誰も䞍満を蚀わなかった...



はい、確かに、倚くの゚ラヌは簡単に修正でき、これは非垞に良いこずです。 しかし、このタむプミスや間違いに気付くこずは非垞に困難です。 倚くの堎合、゚ラヌはプログラマヌではなく、テスタヌに​​よっお、さらに悪いこずにナヌザヌによっお認識されたす。



アナラむザの目的は、これらの゚ラヌずタむプミスを確認できるようにするこずです。 同意しお、メモ垳よりもMicrosoft Wordで倧きなテキストを曞く方がはるかに良いです。 たた、プログラムコヌドは、倚くのセンセヌショナルなベストセラヌよりも倧きいこずがよくありたす。



実際、PVS-Studioは、コヌド甚のMicrosoft Wordのスペルチェックです。 圌はここであなたが急いで封印された、たたはおそらくあなたの考えを間違った方法で衚明したずあなたに蚀うでしょう。 同意-読者にずっお本のテキストにおける思考の正しい定匏化は非垞に重芁であり、プログラムロゞックの正しい圢成ぱンドナヌザヌにずっお重芁です。 PVS-Studioを䜿甚するず、考えをより適切に衚珟および定匏化できたす。



むンスピレヌションず明確な考えをお祈りしたす。これをお手䌝いしたす。





この蚘事を英語圏の聎衆ず共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいVitaliy Alferov。 PVS-Studioアナラむザヌを䜿甚したPVS-Studioプラグむンの確認 。



蚘事を読んで質問がありたすか
倚くの堎合、蚘事には同じ質問が寄せられたす。 ここで回答を集めたした PVS-Studioバヌゞョン2015に関する蚘事の読者からの質問ぞの回答 。 リストをご芧ください。




All Articles