
PVS-Studioを使用したプロジェクトのチェックに関する記事を書くとき、原則として、より多くの顧客を追加します。 ここではすべてが公平です。 プログラマーは広告が好きではありませんが、検証しやすい興味深い資料に熱心に応答します。 したがって、ツールを宣伝するのではなく、できることを示すだけです。 ただし、Chromiumコードを3回確認し、3回エラーを見つけましたが、私のメールにはgoogle.comのメールに関する保証はまだありません。 私が間違っていることと、GoogleがまだPVS-Studioを使用していない理由に興味があるので、別の記事を書くことにしました。
この記事には2つの部分があります。 最初はChromiumプロジェクトのインフラストラクチャと統合のニュアンスについて、2番目は見つかった次のエラーを示しています 。
ところで、この記事は英語で公開されています。 英語を話す同僚に転送したい場合は、 このリンクを彼らに教えてください。
Chromiumの開発がなぜ難しく、プログラマー向けのすべてのツールがChromiumプロジェクトで使用できるわけではないのかを知りたいですか? 次に読む...
Chromium開発者はVisual Studioが好きではなく、Makefileを使用しませんが、同時に高品質のコードを持っています。 どうして?
Chromiumのようなプロジェクトの設計は非常に困難です。 実際、このレベルの他のプロジェクトは知らないので、「Chromiumのようなプロジェクト」を書くのは少し厄介です。 いいえ、Linuxカーネル、Visual Studio環境、さらに多くの大規模プロジェクトがあることは明らかです。 しかし、個人的には、Chromiumでのみ「待機」する機会がありました。 そして、学ぶべきことがたくさんあるので、私が見たものはどんなプログラマにとっても非常に興味深いものです。
たとえば、Chromiumを開発する場合、あまりアクティブではありませんが、Visual Studioを使用していることがわかります。 理由は簡単です-Chromiumには膨大な数のプロジェクトが含まれており、Microsoft IDEは率直に言ってそのような量を「引き出しません」。 「あぁ!」と厳しいLinuxoidsが言いました...「私は願っています!!!」しかし、LinuxはChromiumの開発時にMakefileを使用しません。 まったく同じ理由で-標準のGNU makeは非常に多くのプロジェクトを「プルしない」ため、動作が長すぎます。
Chromiumの開発者は何を使用しますか? これはGYP(Generate Your Projects)ビルドシステムです。 これを使用して、.vcxprojファイル(MSBuild / Visual C ++)またはNinjaシステムファイル(これは非常に単純化された高速なメイクファイルです)を生成できます。 そのため、ある種の静的解析を定期的に使用するには、このアセンブリシステムに統合する必要があります。 GoogleでPVS-Studioを販売するために行ったこと。
Chromiumプロジェクトは、サードパーティのライブラリを考慮したC / C ++言語のソースコードのサイズが500 MBを超え、さまざまなアーキテクチャと構成の何百ものテストコンピューターで数万の自動テストによって並行してコードの変更がチェックされるという事実が注目に値します。 このプロジェクトの開発速度が速いことに注目できます:2012年には、Chromiumリポジトリに900人以上のユニークな著者による48,000件以上のリビジョンが作成されました。これは、プロジェクトのアクティブな参加者それぞれから平均11分ごとに1リビジョン、週に1リビジョンに相当します。
このサイズと開発速度のプロジェクトでは、エラー検出器の汎用性、精度、効率、およびテストシステム全体に特に厳しい要件が課されます。 検出器のエラー、不正確性、および非効率性の多くは、Chromiumをテストするときに最初に正確に発見されました。 特に、オープンソースコードなしで市販の検出器を使用することは不合理に難しいことが判明しました-それらはしばしばプロジェクトの基本的なプリミティブでさえ誤って処理しましたが、これらの欠点を修正するには検出器の新しいバージョンのリリースまで長い待ち時間が必要でした。
PVS-Studioはオープンソースプロジェクトではありませんが、柔軟性を拒否することはできません。 そのため、Chromiumの例で、アセンブリシステムに統合して、このような大規模プロジェクトを問題なくテストできることを示したかったのです。
定期的なチェックのためにPVS-StudioをChromiumビルドシステムに統合する方法
PVS-Studioの動作原理に関する一般情報
PVS-Studioディストリビューションキットには、PVS-Studio.exeコマンドラインアナライザーと、このコマンドラインアナライザーをサポートされている開発環境(Microsoft Visual StudioおよびEmbarcadero RAD Studio)の1つに統合するIDEプラグインの2つの主要コンポーネントがあります。
コマンドラインアナライザーは、その動作原理により、コンパイラーと非常によく似ています。このファイルの元のコンパイルパラメーターを含むパラメーターを持つチェック済みファイルごとに呼び出されます。 次に、アナライザーは、必要な外部プリプロセッサーを呼び出し(チェックするファイルの作成に使用したコンパイラーに従って)、結果の前処理済み一時ファイル(iファイル)を直接分析します。 すべてのincludeおよびdefineディレクティブが展開されているファイル。
ただし、PVS-Studio.exeアナライザーの使用はIDEプラグインに限定されません。 前述のように、コマンドラインアナライザーの使用方法は、コンパイラーに非常に近いです。 したがって、その呼び出しは、必要に応じて、コンパイラ自体とともにアセンブリシステムに直接統合できます。 たとえば、プロジェクトがgccを使用してEclipse IDEでビルドされている場合、メイクファイルにPVS-Studio検証を埋め込むことができます。
静的解析をアセンブリプロセスに直接統合するには、C / C ++コンパイラへの直接呼び出しの横にあるアセンブリスクリプトにPVS-Studio.exe呼び出しを埋め込み、コンパイラに渡される同じパラメーター(および分析結果の出力を制御するいくつかの追加パラメーター)をアナライザーに渡す必要があります) この要件は、このファイルに対応する特定のパラメーターを使用して、スキャンされる各ファイルに対して静的アナライザーを実行する必要があるという事実によるものです。 そして、これはすべてのプロジェクトソースファイルの自動クロールが行われる場所で最も便利に行われます。
PVS-Studio.exe静的アナライザーでChromiumプロジェクトを確認する
前述のように、ChromiumはGYP(Generate Your Projects)ビルドシステムを使用して開発されており、さまざまなオペレーティングシステムとコンパイラのネイティブプロジェクトファイルを取得できます。 なぜなら 現時点では、PVS-StudioアナライザーはWindows OSのみをサポートしており、Visual C ++ 10コンパイラーを使用してChromiumを構築するための可能なオプションを検討します。このコンパイラー(cl.exe)はVisual Studio統合開発環境の一部として提供され、無料のWindowsパッケージとは別にインストールすることもできますSDK
MSBuildプロジェクトファイルの使用
Chromiumで使用されるGYPシステムを使用すると、Visual C ++コンパイラ(cl.exe)を使用してChromiumを構築するためのMSBuildプロジェクトファイル(vcxproj)を取得できます。 MSBuildアセンブリシステムは、Windowsオペレーティングシステムの標準コンポーネントの1つである.NET Frameworkの一部です。
PVS-StudioアナライザーでChromiumプロジェクトをテストする最も簡単な方法は、Visual StudioのネイティブIDEプラグインを使用することです。 MSBuildプロジェクトファイルは、Visual Studio開発環境で検証するために開くことができます。 同時に、PVS-Studio IDEプラグインは、各プロジェクトファイルに関する必要な情報をすべて自動的に収集し、PVS-Studio.exe静的アナライザーを起動します。 Visual Studio Express Edition開発環境の無料バージョンは、IDEプラグインの使用をサポートしていないことに注意してください。
vcxprojプロジェクトファイルのアセンブリと検証は、Visual Studio環境を使用せずに、MSBuildアセンブリシステムを使用して(またはコマンドラインユーティリティMSBuild.exeを使用して)直接実行することもできます。 このモードで静的アナライザーを使用してプロジェクトをテストするには、コマンドラインアナライザーPVS-Studio.exeを各プロジェクトファイルに直接統合する必要があります(同様のアナライザー呼び出しを含む共通のpropsファイルをすべてのプロジェクトファイルにインポートすることもできます)。
MSBuildでは、アセンブリスクリプト(特にvcxprojプロジェクトファイル)から実行可能ファイルを直接呼び出すことができますが、特別なプラグインラッパーを使用して、標準プロジェクトのコンパイラやリンカーなどのアセンブリツールを呼び出すことができます。 (MSBuild用語ではビルドタスクと呼ばれます)。 PVS-Studioディストリビューションキットには、MSBuildスクリプト用の同様のアセンブリタスクモジュールと、標準のvcxprojプロジェクトに直接インポートして静的分析を統合できるProps(プロパティシート)ファイルが含まれています。
忍者プロジェクトファイルの使用
cl.exeコンパイラを使用してWindowsでChromiumをビルドすることも、ニンジャビルドシステムスクリプトを使用して実行できます。これはGYPプロジェクトジェネレーターからも取得できます。
上記のように、PVS-Studioの静的解析をアセンブリプロセスに直接統合するには、ソースファイルがコンパイルされるときにアセンブリシステムによってクロールされる場所にPVS-Studio.exe呼び出しを埋め込む必要があります。
Ninjaシステムファイルの場合、収集されたファイルはobjファイルの依存関係として自動生成された* .ninjaファイルにハードコードされているため、この統合方法は困難です。 この点で、このアセンブリステップで分析を統合するには、このステップに対応するアセンブリルール(一般的なbuild.ninjaファイルで説明)を変更する必要があります。これらはssとcxxです。 すべてのソースファイルを走査するときに使用されるのはこれらのルールです。
現時点では、PVS-Studio.exe呼び出しをccおよびcxxルールに直接追加することはできませんでした。 忍者のビルドルールでは、1つのコマンド変数のみを使用して、実行するコマンドを決定できます。 同時に、ドキュメントによると、コマンド変数には&&文字で区切られた複数のコマンドを含めることができますが、たとえば、既存のルールに追加する場合:
command = ninja -t msvc -e $arch -- $cc /nologo /showIncludes /FC @$out.rsp /c $in /Fo$out /Fd$pdbname
PVS-Studio.exe:
PVS = "PVS-Studio.exe" ... command = ninja -t msvc -e $arch -- $cc /nologo /showIncludes /FC @$out.rsp /c $in /Fo$out /Fd$pdbname && $PVS –cfg "c:\test.cfg"
, ninja, , , –t msvs cl.exe ($cc). , $PVS , && PVS-Studio.exe.
, -, ninja, PVS-Studio.exe, command (cc cxx). , Chromium PVS-Studio .
Command-line PVS-Studio.exe .
, PVS-Studio.exe (.. IDE ) — . PVS-Studio.exe cl-params, «» . PVS-Studio.exe (, cl.exe), , ( /P cl.exe).
, - , C/C++ . , , include , , precompiled . (, pch ), «cannot open include file».
IDE PVS-Studio precompiled , , include , pch ( ). PVS-Studio.exe , . , –I (/I) .
Chromium , .. , precompiled , Includes , h , . PVS-Studio (.. ) .
. , precompiled headers Chromium PVS-Studio .
?
«» (raw) . PVS-Studio Standalone ( ). .
PVS-Studio Chromium
, PVS-Studio Chromium?
- precompiled headers.
- Ninja.
- Ninja PVS-Studio Wrapper ( PVS-Studio), PVS-Studio.
- – – PVS-Studio Standalone .
Chromium , . PVS-Studio . . , - , . , Chromium. , . Chromium .
. , . , Chromium , , , , PVS-Studio.
, , Chromium: , . -, . -, Chromium. , , , . , ( ) , . , Chromium .
(, :)
static SECStatus ssl3_SendEncryptedExtensions(sslSocket *ss) { static const unsigned char P256_SPKI_PREFIX[] = { 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04 }; .... if (.... || memcmp(spki->data, P256_SPKI_PREFIX, sizeof(P256_SPKI_PREFIX) != 0)) { PORT_SetError(SSL_ERROR_INVALID_CHANNEL_ID_KEY); rv = SECFailure; goto loser; } .... }
PVS-Studio ( Network Security Services): V526 The 'memcmp' function returns 0 if corresponding buffers are equal. Consider examining the condition for mistakes. ssl3con.c 10533
- memcmp() .
«sizeof(P256_SPKI_PREFIX) != 0» . - 1.
:
if (.... || memcmp(spki->data, P256_SPKI_PREFIX, sizeof(P256_SPKI_PREFIX)) != 0)
'i' 1.
void SkCanvasStack::pushCanvas(....) { .... for (int i = fList.count() - 1; i > 0; --i) { SkIRect localBounds = canvasBounds; localBounds.offset(origin - fCanvasData[i-1].origin); fCanvasData[i-1].requiredClip.op(localBounds, SkRegion::kDifference_Op); fList[i-i]->clipRegion(fCanvasData[i-1].requiredClip); } .... }
? :) .
PVS-Studio ( Skia Graphics Engine): V501 There are identical sub-expressions to the left and to the right of the '-' operator: i — i SkCanvasStack.cpp 38
[i — 1]. , [i-i]. . , .
«»
Code* Code::FindFirstCode() { ASSERT(is_inline_cache_stub()); DisallowHeapAllocation no_allocation; int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET); for (RelocIterator it(this, mask); !it.done(); it.next()) { RelocInfo* info = it.rinfo(); return Code::GetCodeFromTargetAddress(info->target_address()); } return NULL; }
PVS-Studio (Chromium): V612 An unconditional 'return' within a loop. objects.cc 10326
. , . , . .
:
int SymbolTable::Symbolize() { .... if (socketpair(AF_UNIX, SOCK_STREAM, 0, child_fds[i]) == -1) { for (int j = 0; j < i; j++) { close(child_fds[j][0]); close(child_fds[j][1]); PrintError("Cannot create a socket pair"); return 0; } } .... }
PVS-Studio ( tcmalloc): V612 An unconditional 'return' within a loop. symbolize.cc 154
, , . , :
if (socketpair(AF_UNIX, SOCK_STREAM, 0, child_fds[i]) == -1) { for (int j = 0; j < i; j++) { close(child_fds[j][0]); close(child_fds[j][1]); } PrintError("Cannot create a socket pair"); return 0; }
—
class CONTENT_EXPORT EventPacket { .... InputEvents::const_iterator begin() const { return events_.end(); } InputEvents::const_iterator end() const { return events_.end(); } .... protected: InputEvents events_; .... };
PVS-Studio (Chromium): V524 It is odd that the body of 'end' function is fully equivalent to the body of 'begin' function. event_packet.h 36
beign() end() . , begin() :
InputEvents::const_iterator begin() const { return events_.begin(); }
rdtsc()
__inline__ unsigned long long int rdtsc() { #ifdef __x86_64__ unsigned int a, d; __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d)); return (unsigned long)a | ((unsigned long)d << 32); #elif defined(__i386__) unsigned long long int x; __asm__ volatile ("rdtsc" : "=A" (x)); return x; #else #define NO_CYCLE_COUNTER return 0; #endif }
PVS-Studio ( SMHasher): V629 Consider inspecting the '(unsigned long) d << 32' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. Platform.h 78
. , , long 32-. "(unsigned long)d << 32" . , :
return (unsigned long long)a | ((unsigned long long)d << 32);
break
'break' . . .
:
static v8::Handle<v8::Value> toV8Object(....) { switch (extension->name()) { .... case WebGLExtension::WebGLCompressedTextureATCName: extensionObject = toV8(....); referenceName = "webGLCompressedTextureATCName"; case WebGLExtension::WebGLCompressedTexturePVRTCName: extensionObject = toV8(....); referenceName = "webGLCompressedTexturePVRTCName"; break; } .... }
PVS-Studio ( WebKit):
- V519 The 'extensionObject' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 222, 225. V8WebGLRenderingContextCustom.cpp 225
- V519 The 'referenceName' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 223, 226. V8WebGLRenderingContextCustom.cpp 226
:
bool ScriptDebugServer::executeSkipPauseRequest(....) { const char* v8MethodName; switch (request) { case ScriptDebugListener::NoSkip: return false; case ScriptDebugListener::Continue: return true; case ScriptDebugListener::StepInto: v8MethodName = stepIntoV8MethodName; case ScriptDebugListener::StepOut: v8MethodName = stepOutV8MethodName; } .... }
PVS-Studio ( WebKit): V519 The 'v8MethodName' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 412, 414. ScriptDebugServer.cpp 414
. , .
:
int linux_get_device_address (...., uint8_t *busnum, uint8_t *devaddr, ....) { .... *busnum = __read_sysfs_attr(ctx, sys_name, "busnum"); if (0 > *busnum) return *busnum; .... }
PVS-Studio ( LibUSB): V547 Expression '0 > * busnum' is always false. Unsigned type value is never < 0. linux_usbfs.c 620
'busnum' , uint8_t. , (0 > *busnum) .
. . .
, Chromium
, PVS-Studio Chromium. , PVS-Studio . , - . – Chromium. PVS-Studio !
, . – , .
P.S. NDA , .