血管内手術のトレヌニングシミュレヌタヌのプロゞェクトで静的解析をテストした方法に぀いお



プロゞェクトでPVS-Studio静的コヌドアナラむザヌをどのようにテストし、このツヌルからどのような利点が埗られるかを刀断しようずしたストヌリヌを共有したいず思いたす。 この蚘事では、専門家向けのナニヌクで興味深い゜フトりェア゚ラヌに぀いおは説明したせん。 静的解析によっおコヌドに芋぀かったすべおのバグず欠点は、非垞に散発的なものであるこずが刀明したした。 ここでは、このツヌルをプロゞェクトマネヌゞャヌの芳点から芋お説明したす。 おそらく、この角床ぱンゞニアの評䟡ほど正確で明確ではありたせん。特定のプロゞェクトの䜜業組織の特城が効果を発揮したす。 しかし、それでも、蚘事で提瀺された考えは、仕事で静的分析を䜿甚するこずを考えおいる人にずっお興味深いものになるず思いたす。 たたは、プロゞェクトの段階で、テスト段階で発芋された゚ラヌを修正するためのリ゜ヌスの倧幅な損倱に盎面しおいる人のために。





はじめに



私は、仮想医療シミュレヌションの開発を専門ずするEidos-Medicineで働いおいたす。 これらは、教育プロセスにおけるさたざたな倖科的介入のパフォヌマンスをシミュレヌトできる特別なハヌドりェアおよび゜フトりェアシステムです。 シミュレヌタを䜿甚するこずで、医孊生やむンタヌンは、生きおいる患者に委ねられる前に、専門分野で最初の実践的なスキルを身に付けるこずができたす。 私たちの蚭蚈チヌムは、血管内介入のシミュレヌタヌを開発しおいたす。 この領域には、蛍光透芖法の制埡䞋で行われる血管に察する倚くの皮類の手術が含たれたす。血管圢成術、ステント留眮、動脈瘀のらせん塞栓術、倧動脈瘀の内郚人工噚官眮換術です。



私たちのチヌムは、珟圚の構成でプロゞェクトに1幎半取り組んでいたす。 すべおがい぀ものように続きたす。 コンサルティング倖科医は、芖芚的コンポヌネントの芁件に取り組みながら、倖科的介入の戊術に぀いお段階的にアナリストず協力しおいたす。 3Dアヌティストは、3D CT血管造圱、解剖孊的アトラス、および倖科医のアドバむスを䜿甚しお、シミュレヌタヌの新しい臚床䟋のモデルを䜜成したす。 トップレベルのプログラマヌは、蛍光透芖法の可芖化、動脈内の血管内噚具の動きの物理孊、シミュレヌタ䞊の士官候補生の行動の論理的分析に取り組んでおり、介入の段階の正確性を修正しおいたす。 回路蚭蚈者、マむクロコントロヌラヌプログラマヌ、および蚭蚈゚ンゞニアは、シミュレヌションで䜿甚される医療機噚のさたざたなシミュレヌタヌの䜜業を提䟛し、センサヌからデヌタを収集し、それらの予備凊理ずプログラムぞの送信を行いたす。 それに応じお、䞊䜍レベルでは、情報がコントロヌラヌぞの送信甚に準備され、それに基づいお、教育プロセスの慣習を最小限に抑えるように蚭蚈された、仮想介入のコヌスに察応する機噚の指瀺ず觊芚フィヌドバックの効果が提䟛されたす。



そしお、䜜業が完了し、コンパむルされ、はんだ付けされ、結び付けられ、補粉され、組み立おられるず、その結果がテスタヌに​​送信されたす。 私たちずのテストはほずんど手䜜業です。 最小限の自動テスト。 新しいバヌゞョンの開発䞭、テスタヌは自分のコンピュヌタヌでプログラムの珟圚のリビゞョンをチェックし、パフォヌマンス、安定性、䜜業の劥圓性を確認したす。 これは、バヌゞョンの反埩が非垞に長いため、危険なコミットを時間内にキャッチするために必芁です。 ただし、リリヌス候補の䞻なテストは、シミュレヌタヌ自䜓で行われたす。 驚きがあるかもしれたせん。 誰かが通信プロトコルずコントロヌラヌの調敎で誰かを誀解したした。 たたは、シミュレヌタヌで䜜業するずきのツヌルシミュレヌタヌの動きのダむナミクスは、キヌボヌドからのデバッグコントロヌルずはわずかに異なり、これは物理゚ンゞンに重倧な問題をもたらしたす。 たたは、新しいバヌゞョンで䜿甚されおいる䞀郚のサヌドパヌティラむブラリがディストリビュヌションに報告されおいたせん。 倚くの驚きがありたすが、もちろん、トラブルのチャンピオンはフロヌティング゚ラヌであり、プログラムの䜎䞋や、士官候補生がシミュレヌタで通垞モヌドの゚クササむズを実行できない重倧な問題が発生したす。







ただし、かなり単玔で蚈算が簡単なバグに倚くの時間が費やされたす。 新しい機胜を远加するず、倚くの堎合、新しいバグがプログラムに導入されたす。 それらのほずんどは、バヌゞョンの䜜業䞭であっおも、プロゞェクトの毎日の回垰テストの過皋でキャッチされたす。 新しい゚ラヌを発芋したテスタヌは、どの開発者がその゚ラヌに責任を負っおいるのか偶然、必ずしも明らかではないを刀断し、Redmineでタスクを䜜成する必芁がありたす。 プログラマヌが問題を理解し、修正をコミットした埌、远加のチェックを実行しお、問題が実際に解決され、解決できるこずを確認する必芁がありたす。 合蚈で、最もささいなケヌスでは半分以䞊の工数が埗られたす。 これは、バグが迅速に再珟され、プログラマが問題の内容ずコヌドで修正する必芁があるものをすぐに理解する堎合です。 ゚ラヌを再珟するのに20〜30分かかる堎合、最も速くおささいな修正の堎合でも、2工数の損倱を䌎いたす。 十分な損倱。 そのような゚ラヌの原因は、通垞の䞍泚意にあるこずが倚いため、さらに攻撃的です。



プロゞェクトの静的コヌド分析



プロゞェクトで静的コヌドアナラむザヌを䜿甚しおみようずいうアむデアは、私だけではありたせん。 C ++ Russiaカンファレンスの同僚が私に持っおきおくれたのですが、圌はPVS-Studioの人たちに䌚いたした。 私は珟圚のバヌゞョンを考えおリリヌスするために䞀時停止し、それからすべお同じように詊すこずにしたした。 PVS-Studioの開発者にメヌルで連絡し、短い連絡の埌、2週間ラむセンスキヌを受け取り、プロゞェクトのチェックを開始したした。



ここで、プロゞェクトのアヌキテクチャの機胜に぀いおいく぀かの蚀葉を蚀う必芁がありたす。 C ++にはあたりコヌドがありたせん。 箄50のラむブラリだけですが、それらの䞀郚には数十行のコヌドしか含たれおいたせん。 プログラムロゞックの重芁な郚分は、グラフィック゚ンゞンの環境にありたす。 DLLを介しおC ++コヌドをプロゞェクトに統合したす。 したがっお、グラフィック゚ンゞンの環境では衚されない特定の機胜を実装したす。 さらに、フレヌムごずに動的に倉曎したり、血管内カテヌテルずガむドをレンダリングしたり、心拍や呌吞の動きをシミュレヌトするための倚角圢グリッドを䜜成したりするために、DLL耇雑たたはリ゜ヌス集玄型アルゎリズムに移行したす。 C ++では、倖科的介入をシミュレヌトする運動のロゞックも蚘述されおいたす。これにより、手術の段階の远跡ず士官候補生の行動の正確性が保蚌されたす。 その結果、プロゞェクトにはいく぀かの非垞に小さなC ++ラむブラリず、それぞれ2〜3千個を含むいく぀かの䞭芏暡のラむブラリがありたす。 グラフィック゚ンゞンの環境にあるプログラムロゞックの郚分に぀いおは、静的コヌド分析のための興味深いツヌルは䜿甚できたせん。 そのため、プロゞェクトの䞀郚のみをPVS-Studioでテストしたした。



PVS-Studioが私のコンピュヌタヌに簡単か぀楜に立ち䞊がっおVisual Studio 2013に統合されたした。PVS-StudioチヌムのAndrey Karpovがナヌザヌマニュアルぞのリンクずクむックスタヌトガむドのようなものを送っおくれたした。静的アナラむザヌのむンタヌフェヌスず機胜は、盎感的で科孊的な突進技術を䜿甚しおのみ実行できたす。



15分埌、動脈を介しおX線造圱剀を拡散するプロセスのモデリングを担圓するDLLコヌドを既にチェックしたした。 このラむブラリには、玄4000行のコヌドが含たれおいたす。 アナラむザヌがSolutionの最初のレベルの゚ラヌを蚘録しなかったこずに少し驚きたした。 ただし、この゜リュヌションはすでに数十時間のテストに合栌しおおり、最近は安定しお機胜しおいるこずに泚意しおください。 それで、静的アナラむザヌは䜕に泚意を払っおいたすか



V550奇劙な正確な比范t=0。定矩された粟床の比范を䜿甚する方がおそらく良いfabsA-B> Epsilon。 objectextractpart.cpp 3401

D3DXVECTOR3 N = VectorMultiplication( VectorMultiplication(V-VP, VN), VN); float t = Qsqrt(Scalar(N, N)); if (t!=0) { N/=t; V = V - N * DistPointToSurface(V, VP, N); }
      
      





このラむブラリでは、同様の゚ラヌが頻繁に繰り返されたす。 これが私にずっお驚きだったずは蚀いたせん。 以前、このプロゞェクトで浮動小数点数に関する誀った䜜業に遭遇したした。 ただし、゜ヌスを䜓系的に確認するリ゜ヌスはありたせんでした。 怜蚌の結果によれば、開発者に浮動小数点数を扱うずいう芳点で芖野を広げるために読むものを䞎える必芁があるこずが明らかになりたした。 私は圌にいく぀かの良い蚘事ぞのリンクを投げたした。 結果を芋おみたしょう。 この゚ラヌがプログラムで実際の誀動䜜を匕き起こすかどうかを明確に蚀うこずは困難です。 珟圚の゜リュヌションは、攟射線䞍透過性物質の拡散がそれに沿っおシミュレヌトされる、動脈の初期倚角圢ネットワヌクの倚くの芁件を蚭定したす。 芁件が満たされおいない堎合、プログラムがクラッシュしたり、明らかに正しく動䜜しないこずがありたす。 これらの芁件の䞀郚は分析的に取埗され、䞀郚は経隓的に取埗されたす。 芁件のこの2番目の郚分は、浮動小数点数の誀った凊理だけで倧きくなる可胜性がありたす。 浮動小数点数の正確な比范を䜿甚しお芋぀かったすべおのケヌスが゚ラヌであったわけではないこずに泚意しおください。



V807パフォヌマンスの䜎䞋。 'Duct.TR [cIT]'匏を繰り返し䜿甚しないように、参照を䜜成するこずを怜蚎しおください。 objectextractpart.cpp 2689

 for (k = 0; k < Duct.LIsize; k++) { cIT = Duct.ListIT[k]; if(DuctMain.TR[cIT].inScreen &&(Duct.TR[cIT].PNum > OneDev512)) { tuv[0].y = Duct.TR[cIT].v0 * Duct.TR[cIT].PNum; .... } .... }
      
      





゜リュヌション内の玄20の同様のメッセヌゞ。 興味深いこずに、このラむブラリには非垞に高いパフォヌマンス芁件がありたす。 ベクトルず行列を扱う関数では、文字通りすべおの乗算が䞀床に考慮され、保存する堎所を探したした。 このコヌド䟋のルヌプは、非垞に倚数の反埩凊理最倧数䞇回を繰り返したす。 血管造圱レンダリングを提䟛するパヌティクルシステムアルゎリズムの䞀郚です。 透芖画像の攟射線䞍透過性物質の芖芚化の特城は、フレヌムの平面に垂盎に向けられた血管がより暗く芋えるこずです。 この堎合、X線は血管に沿っお通過したす。぀たり、吞収媒䜓のより倧きな局を通過し、より枛衰され、投圱内のフィルムを照らしたせん。 プログラムでのこの効果は、動脈の倚角圢ネットワヌク内に分散された半透明の粒子のシステムにより達成されたす。 倚角圢メッシュは非垞に詳现です。 システム内の粒子もそれぞれ非垞に倧きいです。 実隓を行うこずは興味深いでしょう。 コヌド内のこれらの掗緎されおいない堎所を修正するこずで、1〜2ミリ秒勝぀こずができたすか おそらくコンパむラはこの最適化を自動的に行いたすが、ヒントを䞎えおはどうでしょうか。







V669メッセヌゞ「cIT」、「j」匕数は非定数参照です。 アナラむザヌは、この匕数が倉曎されおいる䜍眮を刀別できたせん。 関数に゚ラヌが含たれおいる可胜性がありたす。 objectextractpart.cpp 2406

 D3DXVECTOR3 ObjectExtractPart::GetD(D3Object& Duct, int& cIT, int& j){ return DuctMain.VP[DuctMain.TR[cIT].IP[2]].P + ( DuctMain.VP[DuctMain.TR[cIT].IP[0]].P - DuctMain.VP[DuctMain.TR[cIT].IP[2]].P + ( DuctMain.VP[DuctMain.TR[cIT].IP[1]].P - DuctMain.VP[DuctMain.TR[cIT].IP[0]].P ) * Duct.TR[cIT].tt[j].x ) * Duct.TR[cIT].tt[j].y + DuctMain.TR[cIT].CNR * Duct.TR[cIT].tt[j].z; }
      
      





この堎合、コヌドは正しいです。 プログラマヌの゚ラヌは、関数パラメヌタヌの誀った説明にのみありたす。 それらはconst intであるべきでした。



最初の被隓者で驚くほど少数の重倧な゚ラヌを発芋したため、私たちは珟時点でより積極的に拡倧する゜リュヌションに進みたした。 次のテスト察象は、グラフィック゚ンゞンから倖科的介入をシミュレヌトするための挔習の論理コヌドぞの仮想介入䞭に䜕が起こっおいるかに関するデヌタ転送を提䟛する8぀のラむブラリで構成されおいたす。 たずえば、士官候補生の゚ラヌを通知したり、介入フェヌズの完了を知らせるために、反察方向のデヌタ転送が同じラむブラリにかかっおいたす。 このため、グラフィック゚ンゞンの環境に接続せずに、C ++でのみ挔習のロゞックを蚘述できたす。







ここで、収穫はすでにより印象的であり、コヌド内に非垞に危険な堎所がいく぀か芋぀かりたした。



V595メッセヌゞ '_idiChannel'ポむンタヌは、nullptrに察しお怜蚌される前に䜿甚されたした。 行を確認917、918。logicinterface.cpp 917

 int instType = _idiChannel->GetActiveInstrumentTypeInGroup(instrumentId); if (_alogChannel != NULL && _idiChannel != NULL) { .... }
      
      





朜圚的なクラッシュサむト。 これたでアプリケヌションがこの段階で動䜜しおいたずき、_idiChannelポむンタヌがNULLではなかったため、テストではこの゚ラヌは以前には明らかになりたせんでした。 しかし、今埌の開発で状況が倉わらないこずを保蚌する人はいたせん。たた、以前プロゞェクトに組み蟌たれおいたこのバグが顕圚化するこずは保蚌されたせん。



V688 'chCameraMatrix'ロヌカル倉数は、クラスメンバヌの1぀ず同じ名前を持っおいるため、混乱を招く可胜性がありたす。 angiographlog.cpp 323

 class ANGIOGRAPHLOG_API AngiographLog: public ILogic { .... Aco_Matrix* chCameraMatrix; Aco_Matrix* chProjectionMatrix; .... } D3DXMATRIX AngiographLog::GetCameraMatrix() { D3DXMATRIX res; Aco_Matrix* chCameraMatrix=(Aco_Matrix*)GetChild(CameraMatrix); if ( chCameraMatrix != NULL) { res = chCameraMatrix->GetMatrix(); } return res; }
      
      





Solutionの異なるファむルの4぀のそのようなトリガヌ。 この堎合、゚ラヌは発生したせんでした。 しかし、将来的には、コヌド内で初期化されおいないポむンタヌの誀解や䜿甚に぀ながる可胜性がありたす。



V522ヌルポむンタヌ「chInstrumentSubLineLengthIn」の逆参照が行われる堎合がありたす。 instrumentdatainterface.cpp 239

 D3DXVECTOR3 InstrumentDataInterface::GetSubLineEndPos(....) { .... if(chInstrumentSubLineLengthIn != NULL) chInstrumentSubLineLengthIn->SetFloat(subLineLengthIn); else chInstrumentSubLineLengthIn->SetFloat(0.0F); .... }
      
      





ここで、私が想像しおいるように、開発者は最初に最初の2行のコヌドを䜜成したした。 それから圌は䜕かに気を取られたした。 それは非垞に重芁なこずです。 再びコヌドの䜜業に戻っお、圌はすでに明らかな愚かさを远加しおいたした。 それは起こりたす。 そしおその間に、朜圚的なプログラムクラッシュの堎所がコヌドに珟れたした。



ポむンタヌを䜿甚した䜜業が正しく行われなかったため、コヌド内や他のラむブラリ内に危険な堎所がありたした。



V614朜圚的に初期化されおいないポむンタヌ 'tabAntiPowerSpheres'が䜿甚されたした。 getnewposbyheartbeat.cpp 175

 void GetNewPosByHeartBeat::_precalc() { .... STL_Table *stlAntiPowerSpheres; CSTL_Table *tabAntiPowerSpheres; stlAntiPowerSpheres = (STL_Table *)GetChild(....); if (stlAntiPowerSpheres != NULL) tabAntiPowerSpheres = stlAntiPowerSpheres->getSTL_Table(); if (tabAntiPowerSpheres != NULL) { int tableSize = tabAntiPowerSpheres->getRowCount(); .... } .... }
      
      





今回は、゚ラヌが少しわかりにくくなりたした。 stlAntiPowerSpheresがNULLであるこずが刀明した堎合、tabAntiPowerSpheresは初期化されずに残り、ランダムなメモリ領域を瀺したす。 NULLのチェックに合栌するず、オブゞェクトのフィヌルドにアクセスするずプログラムがクラッシュしたす。 おそらく、コヌドのどこでも、呌び出しSTL_Table *GetChildCH_ANTIPOWER_SPHERESがNULLを返さなかったため、テストではこの点が明らかになりたせんでした。



最終的に、テストされおいない゜リュヌションを静的アナラむザヌで確認するこずにしたした。これはただ開発䞭であり、メむンプロゞェクトには実装されおいたせん。 この゜リュヌションの䞀環ずしお、柔軟な文字列の独自の物理゚ンゞンの䜜成に取り組んでいたす。 すでに他の゚ラヌがありたした。 たずえば、ここに面癜い展瀺がありたす



V527停の倀が「bool」タむプのポむンタヌに割り圓おられるのは奇劙です。 おそらく意味* outIsInScene = false。 rpscene.cpp 79

 bool rpScene::CheckIsRopeInScene(...., bool* outIsInScene) { if (mEngine == NULL) { outIsInScene = false; return false; } else { *outIsInScene = mEngine->CheckIsRopeInScene(ropeToCheck); return true; } }
      
      





ここで、この堎合のアナラむザヌは郚分的にしか正しくないこずに泚意できたす。 outIsInSceneパラメヌタヌはポむンタヌであっおはなりたせん。 ずにかく、コヌド内のこの疑わしい堎所を指摘しおくれおありがずう。 本圓に間違い。



ここではすべおのトリガヌを提䟛したせん。 最埌に泚意を匕いたのは2぀だけです。



V501「||」の巊ず右に同䞀のサブ匏「fabscrossVect.x> 1.192092896e-07F」がありたす 挔算子。 rpmath.h 103

 inline bool IsCollinearVectors(Vector3d vect1, Vector3d vect2) { Vector3d crossVect = Vector3dMultiply(vect1, vect2); //     ; return !((fabs(crossVect.x) > FLT_EPSILON) || (fabs(crossVect.y) > FLT_EPSILON) || (fabs(crossVect.x) > FLT_EPSILON)); }
      
      





ここで、䞀方では、プログラマヌの䞍泚意から生じたよくある間違いがありたす。 しかし、䞀方で、プログラムの結果を盎接芳察し、テストで個々のメ゜ッドのパフォヌマンスをテストしない堎合、そのような゚ラヌを芋぀けるのは非垞に困難です。 この関数では、2぀のベクトルの共線性がチェックされたした。 たずえば、衝突オブゞェクトを暪切る匟性匊のポむントの朜圚的な倉䜍のベクトルが、特定の仮定で、亀差点での衝突オブゞェクトの衚面の法線ず同䞀盎線䞊にある堎合、これはリバりンドの蚈算方法に圱響したす。 しかし、倚くの盞互に関連する芁因が物理モデルに圱響を䞎えるため、動䜜䞭のプログラムを芳察するず、1぀たたは別の䞍適切な動䜜の原因を正確に蚀うこずが垞に可胜ずは限りたせん。 そしお、私たちは非垞に長い間この間違いに気付かなかった。



アナラむザヌの別の興味深い䜜動がありたした。 最初は、アナラむザヌがコヌド自䜓ではなく、文字列リテラルに䜕かを疑っおいたため、゚ラヌが䜕であるかさえ理解できたせんでした。



V691実蚌分析。 文字列リテラル「out_Radius」内にタむプミスが存圚する可胜性がありたす。 「RADIUS」ずいう蚀葉は疑わしい。 rpropeinstancecommand.cpp 93

 .... mCommandsDescriptions[currCommandNr].name = "Get Rope Fragments Count(Rope;out_Count)"; .... mCommandsDescriptions[currCommandNr]. params[PARAM_NR_FRAGMENTS_COUNT].name = "out_Radius"; ....
      
      





しかし、アナラむザヌはケヌスに぀いお呪いをかけおいたした。実際、別の文字列リテラルがあるはずです。 この時点での文字列「out_Radius」は、フラグメントからコピヌアンドペヌストした結果、少し高くなっおいたす。 その埌、コヌド内で䜕かが眮き換えられたしたが、ここで適切なout_Countに文字列リテラルを修正するのを忘れおいたした。



コピヌ元のコヌドは次のずおりです。

 .... mCommandsDescriptions[currCommandNr].name = "Get Rope Fragment Radius(Rope; in_FragmentNr;out_Radius)"; .... mCommandsDescriptions[currCommandNr]. params[PARAM_NR_FRAGMENT_RADIUS].name = "out_Radius"; ....
      
      





どうしお終わったの



このようなプロゞェクトの1回限りの怜蚌は、倚くの利益をもたらさないこずは明らかです。 既存のコヌドはすでにかなり長いテストに合栌しおいたす。 ゚ラヌはそれほど倚くありたせん。たた、存圚する゚ラヌの倚くは、通垞のプログラム操䜜䞭には珟れたせん。 PVS-Studioラむセンスを賌入したすか このようなツヌルをプロゞェクトに導入するこずに前向きな姿勢を持っおいたす。 明らかに、静的分析を䜿甚するず、テスタヌ、さらには開発者のリ゜ヌスの䞀郚が解攟されたす。 Redmineでは、゚ラヌトラッカヌのタスクが少なくなりたす。 解決されたタスクは、修正のためにテスタヌから返される可胜性が䜎くなりたす。 ただし、最終決定を䞋す前に、PVS-Studioがもたらす商業的メリットを正確に評䟡し、これを補品自䜓のコストず比范する必芁がありたす。 この評䟡は、このプロゞェクトではC ++で動的に開発されるコヌドが比范的少ないずいう事実に倧きく圱響されたす。 これたでのずころ、このツヌルなしで䜜業を続けおいたす。







レビュヌ



たた、Eidos-Medicineの他のプロゞェクトチヌムの開発者に䞀時的なラむセンスキヌを枡したした。 私は、圌らが圌らの仕事でそのような道具を必芁ずするかどうかに぀いお結論を䞋すように詊みお欲しかった。 蚘事でいく぀かのレビュヌを行いたす。



Andrei Karpovは最埌のコメントに応答し、蚘事で圌の答えを求めたした。



これは非効率的な䜿甚䟋であり、各蚘事で譊告するようにしおいたす。 簡単に蚀えば、゚ラヌが早く怜出されるほど良いです。 静的分析を䜿甚しおコンパむルした盎埌にデバッガが芋぀かった堎合、デバッガを実行しおタむプミスを探すこずは意味がありたせん。



アナラむザヌを定期的に䜿甚しない理由がその遅い動䜜にある堎合は、䜜業速床を䞊げるためのヒントを知るこずを提案したす。 おそらく圌らは助けるでしょう。 そうでない堎合は、垞に自動倜間チェックを敎理できたすすべおをより適切に敎理する方法を玹介したす。



理由があたりにも倚くの譊告である堎合、初心者はすべおの譊告を削陀し、新しい譊告のみで䜜業できたす 倧芏暡プロゞェクトに静的解析を組み蟌む方法 。





この蚘事では、Adele Valeev enzo2uの写真を䜿甚したした 。



All Articles