cppcheckの埌、キノコしたす

PVS-Studio、OpenMS



Big Calculatorに぀いおの激しい議論の埌、研究に関連するプロゞェクトから䜕か他のものをチェックしたかったのです。 最初に発芋されたのは、タンパク質質量分析に関連するオヌプンプロゞェクトであるOpenMSです。 このプロゞェクトは、真剣なアプロヌチで曞かれおいるこずが刀明したした。 開発では、少なくずもCppcheckが䜿甚されたす。 したがっお、センセヌショナルなものは䜕も埅぀必芁がありたせんでした。 ただし、Ppp-StudioがCppcheckの埌に怜出できる゚ラヌには関心がありたした。 興味のある方は、この蚘事を読み続けおください。







OpenMSプロゞェクトがありたす。 なぜそれが必芁なのか、私は自分の蚀葉で蚀い盎そうずは思わない。 ただ愚かさを吹き飛ばした。 りィキペディアのりェブサむトから説明の断片を匕甚するだけです



OpenMSは、 タンパク質質量分析のデヌタ分析および凊理のためのオヌプン゜ヌスプロゞェクトであり、2条項のBSDラむセンスの䞋でリリヌスされおいたす。 OpenMSには、プロテオミクスで䜿甚される倚くの䞀般的なデヌタ分析パむプラむン甚のツヌルがあり、信号凊理、特城発芋非アむ゜トヌプ化を含む、1Dスペクトルたたはクロマトグラムレベル、2Dおよび3Dでの芖芚化、マップマッピング、ペプチド同定のアルゎリズムを提䟛したす。 ラベルフリヌおよび同䜍䜓ラベルベヌスの定量化iTRAQ、TMT、SILACなどをサポヌトしおいたす。 さらに、メタボロミクスワヌクフロヌずDIA / SWATHタヌゲット分析もサポヌトしおいたす。



出兞りィキペディア。 Openms



このプロゞェクトは䞭芏暡ですが、非垞に耇雑です。 ゜ヌスコヌドのサむズは20メガバむト以䞊です。 さらに、倚数のサヌドパヌティラむブラリBoost、Qt、Zlibなど。 このプロゞェクトでは、テンプレヌトを非垞に積極的に䜿甚しおいたす。 プロゞェクトの゜ヌスコヌドはSourceForgeからダりンロヌドできたす 。



OpenMSプロゞェクトの開発時には、静的コヌド分析が䜿甚されたす。 「cppcheck.cmake」の存圚ず次の粟神でのコメント

if (i != peptide.size()) // added for cppcheck
      
      





少なくずもCppcheckが䜿甚されるこずを瀺したす。 Cpplintも蚀及されおおり、「cpplint.py」ずいうファむルがありたす。 プロのアプロヌチ。 よくできたした。



ここで、 PVS-Studioアナラむザヌを䜿甚しお芋぀けるこずができるものを芋おみたしょう。



ご泚意 䜕らかの理由で、C ++ファむルには拡匵子* .cが付いおいたす。 したがっお、「* .c」ファむルにあるC ++コヌドの䟋を参照しおも、混乱しないでください。



1. OpenMPの欠点



OpenMPテクノロゞヌを䜿甚するプロゞェクトに出䌚うこずはめったにありたせん。 䞀般的に、アナラむザヌからこれらの蚺断を削陀するために時々考えが蚪れたす。 そのため、OpenMPに関連する譊告を芋たずき、本圓に驚きたした。 過去1幎間、倚数のプロゞェクトをチェックしおきたしたが、そのトピックに関する譊告は䞀床も芋たこずがありたせん。 うわヌ、誰かがただこの技術を䜿甚しおいたす。



発行された譊告の䞭には、誀怜知がありたしたが、このケヌスにも譊告がありたした。

 DoubleReal ILPDCWrapper::compute(....) const { .... DoubleReal score = 0; .... #pragma omp parallel for schedule(dynamic, 1) for (SignedSize i = 0; i < (SignedSize)bins.size(); ++i) { score += computeSlice_(fm, pairs, bins[i].first, bins[i].second, verbose_level); } return score; }
      
      





è­Šå‘ŠPVS-StudioV1205デヌタの競合リスク。 'score'倉数を䜿甚した保護されおいない同時操䜜。 ilpdcwrapper.c 213



金額は間違っおいるず芋なされたす。 倉数 'score'は、異なるスレッドでの同時䜿甚から保護されおいたせん。



他のコメントはそれほど重芁ではありたせんが、ただ泚意する䟡倀がありたす。 䞊列セクション内では、すべおの䟋倖をキャッチする必芁がありたす。 䞊列セクションの制限を超えるず、プログラムが異垞終了する可胜性がありたす。 このトピックの詳现に぀いおは、「 OpenMPず䟋倖 」、「 䞊列セクション内の䟋倖の凊理 」を参照しおください。



throwステヌトメントを䜿甚しお、䟋倖を明瀺的にスロヌできたす。 new挔算子std :: bad_allocを呌び出すずきにも発生する可胜性がありたす。



最初のオプション。 getTheoreticalmaxPosition関数は䟋倖をスロヌする堎合がありたす。

 Size getTheoreticalmaxPosition() const { if (!this->size()) { throw Exception::Precondition(__FILE__, __LINE__, __PRETTY_FUNCTION__, "There must be at least one trace to ......"); } .... } virtual void run() { .... #pragma omp parallel for for (SignedSize i = 0; i < (SignedSize)seeds.size(); ++i) { .... f.setMZ( traces[traces.getTheoreticalmaxPosition()].getAvgMZ()); .... } .... }
      
      





PVS-Studio譊告V1301 'throw'キヌワヌドは、䞊列セクションのtry..catchブロックの倖郚では䜿甚できたせん。 featurefinderalgorithmpickedhelperstructs.h 199



2番目のオプション。 「new」挔算子を呌び出すず、䟋倖が発生する堎合がありたす。

 TraceFitter<PeakType>* chooseTraceFitter_(double& tau) { // choose fitter if (param_.getValue("feature:rt_shape") == "asymmetric") { LOG_DEBUG << "use asymmetric rt peak shape" << std::endl; tau = -1.0; return new EGHTraceFitter<PeakType>(); } .... } virtual void run() { .... #pragma omp parallel for for (SignedSize i = 0; i < (SignedSize)seeds.size(); ++i) { .... TraceFitter<PeakType>* fitter = chooseTraceFitter_(egh_tau); .... } .... }
      
      





PVS-Studio譊告V1302 'new'挔算子は、䞊列セクションのtry..catchブロックの倖郚では䜿甚できたせん。 featurefinderalgorithmpicked.h 1926



他の同様の譊告

2.タむプミス



 std::vector< std::pair<std::string, long> > spectra_offsets; std::vector< std::pair<std::string, long> > chromatograms_offsets; template <typename MapType> void MzMLHandler<MapType>::writeFooter_(std::ostream& os) { .... int indexlists; if (spectra_offsets.empty() && spectra_offsets.empty() ) { indexlists = 0; } else if (!spectra_offsets.empty() && !spectra_offsets.empty() ) { indexlists = 2; } else { indexlists = 1; } .... }
      
      





PVS-Studioの譊告



V501「&&」挔算子の巊偎ず右偎には、同䞀のサブ匏「spectra_offsets.empty」がありたす。 mzmlhandler.h 5288



V501「&&」挔算子の巊右には、同䞀の副次匏「Spectra_offsets.empty」がありたす。 mzmlhandler.h 5292



非垞に奇劙なチェック。 コンテナ「spectra_offsets」は2回チェックされたす。 ほずんどの堎合、これはタむプミスであり、2぀の異なるコンテナspectrum_offsetsおよびchromatograms_offsetsをチェックする必芁がありたす。

 template <typename MapType> void MzMLHandler<MapType>::characters( const XMLCh* const chars, const XMLSize_t) { .... if (optionalAttributeAsString_(data_processing_ref, attributes, s_data_processing_ref)) { data_.back().meta.setDataProcessing( processing_[data_processing_ref]); } else { data_.back().meta.setDataProcessing( processing_[data_processing_ref]); } .... }
      
      





PVS-Studio譊告V523「then」ステヌトメントは「else」ステヌトメントず同等です。 mzmlhandler.h 534



同様のコヌドフラグメントを芋るず、以䞋を䜿甚する必芁があるず結論付けるこずができたす。

倚くのタむプミスは、䟋倖の生成に関連しおいるこずが刀明したした。 間違いはありふれおいたす。 「スロヌ」キヌワヌドは忘れられおいたす。 䞀時オブゞェクトは、ただちに䜜成および砎棄されるだけです。 そのような゚ラヌの䟋

 inline UInt asUInt_(const String & in) { UInt res = 0; try { Int tmp = in.toInt(); if (tmp < 0) { Exception::ConversionError( __FILE__, __LINE__, __PRETTY_FUNCTION__, ""); } res = UInt(tmp); } catch (Exception::ConversionError) { error(LOAD, String("UInt conversion error of \"") + in + "\""); } return res; }
      
      





PVS-Studio譊告V596オブゞェクトは䜜成されたしたが、䜿甚されおいたせん。 「throw」キヌワヌドが欠萜しおいる可胜性がありたすthrow ConversionErrorFOO; xmlhandler.h 247



ここに同じタむプミス

そしお、私が気づいた最埌のタむプミス

 inline typename Value<Pipe>::Type const & operator*() { tmp.i1 = *in.in1; tmp.i2 = *in.in2; tmp.i3 = *in.in2; return tmp; }
      
      





è­Šå‘ŠPVS-StudioV525同様のブロックのコレクションを含むコヌド。 112行目、113行目、114行目の項目「in1」、「in2」、「in2」を確認したす。pipe_joiner.h 112



それは曞かれるべきです

 tmp.i1 = *in.in1; tmp.i2 = *in.in2; tmp.i3 = *in.in3;
      
      





3.奇劙な状態



 CompressedInputSource::CompressedInputSource( const String & file_path, const char * header, MemoryManager * const manager) : xercesc::InputSource(manager) { if (sizeof(header) / sizeof(char) > 1) { head_[0] = header[0]; head_[1] = header[1]; } else { head_[0] = '\0'; head_[1] = '\0'; } .... }
      
      





PVS-Studio譊告V514ポむンタヌのサむズ 'sizeofheader'を別の倀で陀算したす。 論理゚ラヌが存圚する可胜性がありたす。 compressedinputsource.c 52



ポむンタヌのサむズをバむトのサむズで割るず、垞に1より倧きい倀が埗られたす。 少なくずも、私はそれがどこにない掟手なアヌキテクチャを知りたせん。 だからここにある皮の間違いがありたす。



同様の奇劙なチェックは、compressedinputsource.c 104から入手できたす。



4.ロヌカルオブゞェクトぞのリンクを返す



 template <typename TStringSet, typename TSpec> inline Iter<TStringSet, ConcatVirtual<TSpec> > const & operator++(Iter<TStringSet, ConcatVirtual<TSpec> > & me, int) { Iter<TStringSet, ConcatVirtual<TSpec> > before = me; goNext(me); return before; }
      
      





PVS-Studio譊告V558関数は、䞀時ロヌカルオブゞェクトぞの参照を返したすbefore。 iter_concat_virtual.h 277



この関数は、䞀時倉数「before」ぞの参照を返したす。 関数を終了するず、この倉数は砎棄されたす。 砎壊されたオブゞェクトぞの参照を䜿甚するず、予枬できない結果に぀ながりたす。



新しいむテレヌタを䜜成する必芁はないようですが、リンクを保存する必芁がありたす

 Iter<TStringSet, ConcatVirtual<TSpec> > &before = me;
      
      





[PS間違っおいたす。 コメントを参照しおください。]



挔算子 '-'にも同様の問題がありたすiter_concat_virtual.h 310



5.ずさんなコンピュヌティング



 typedef size_t Size; typedef double DoubleReal; void updateMeanEstimate(const DoubleReal & x_t, DoubleReal & mean_t, Size t) { DoubleReal tmp(mean_t); tmp = mean_t + (1 / (t + 1)) * (x_t - mean_t); mean_t = tmp; }
      
      





PVS-Studio譊告V636「1 /t + 1」匏は、暗黙的に「int」型から「double」型にキャストされたした。 分数郚分の損倱を避けるために、明瀺的な型キャストの䜿甚を怜蚎しおください。 䟋double A =doubleX/ Y;。 masstracedetection.c 129



匏「1 /t + 1」は垞にれロたたは1です。 これは、この匏が敎数であるためです。 おそらく、たったく別の結果が埗られるず考えられおいたのかもしれたせん。 プログラムのロゞックはわかりたせんが、次のこずを念頭に眮いおいたようです。

 tmp = mean_t + (1.0 / (t + 1)) * (x_t - mean_t);
      
      





たた、M_PI定数の代わりに、明瀺的な倀が䜿甚されおおり、あたり正確ではないこずも気に入らなかった。 これは間違いではありたせんが、あたり良くありたせん。 䟋

 bool PosteriorErrorProbabilityModel::fit( std::vector<double> & search_engine_scores) { .... incorrectly_assigned_fit_param_.A = 1 / sqrt(2 * 3.14159 * pow(incorrectly_assigned_fit_param_.sigma, 2)); .... }
      
      





è­Šå‘ŠPVS-StudioV624定数3.14159が䜿甚されおいたす。 結果の倀は䞍正確になる可胜性がありたす。 <math.h>のM_PI定数の䜿甚を怜蚎しおください。 posteriorerrorprobabilitymodel.c 92



同様に

6.アレむを海倖に行く



 static const Int CHANNELS_FOURPLEX[4][1]; static const Int CHANNELS_EIGHTPLEX[8][1]; ExitCodes main_(int, const char **) { .... if (itraq_type == ItraqQuantifier::FOURPLEX) { for (Size i = 0; i < 4; ++i) { std::vector<std::pair<String, DoubleReal> > one_label; one_label.push_back(std::make_pair<String, DoubleReal>( String("Channel ") + String(ItraqConstants::CHANNELS_FOURPLEX[i][0]), DoubleReal(ItraqConstants::CHANNELS_FOURPLEX[i][0]))); labels.push_back(one_label); } } else //ItraqQuantifier::EIGHTPLEX { for (Size i = 0; i < 8; ++i) { std::vector<std::pair<String, DoubleReal> > one_label; one_label.push_back(std::make_pair<String, DoubleReal>( String("Channel ") + String(ItraqConstants::CHANNELS_FOURPLEX[i][0]), DoubleReal(ItraqConstants::CHANNELS_FOURPLEX[i][0]))); labels.push_back(one_label); } } .... }
      
      





PVS-Studio譊告V557アレむがオヌバヌランする可胜性がありたす。 「i」むンデックスの倀は7に達する可胜性がありたす。itraqanalyzer.c 232



実際、この゚ラヌはコピヌペヌスト゚ラヌに起因する可胜性がありたす。 しかし、「配列の境界を越えた出口」があるずしたす。 それはもっず怖いですね。 ずにかく、この区分は非垞にarbitrary意的です。 同じ゚ラヌをさたざたな方法で分類できたす。



ここで、「else」ブランチでは、「CHANNELS_EIGHTPLEX」の配列を操䜜する必芁がありたした。 これはコメントによっお蚌明されたす

 else //ItraqQuantifier::EIGHTPLEX
      
      





ただし、コピヌしたコヌドフラグメントは完党には倉曎されおいたせん。 その結果、より小さな配列CHANNELS_FOURPLEXがそこで䜿甚されたす。



ここで同様の゚ラヌ別のコピヌペヌストtmtanalyzer.c 225



別の䟋を考えおみたしょう。

 DoubleReal masse_[255]; ///< mass table EdwardsLippertIterator::EdwardsLippertIterator(const EdwardsLippertIterator & source) : PepIterator(source), f_file_(source.f_file_), actual_pep_(source.actual_pep_), spec_(source.spec_), tol_(source.tol_), is_at_end_(source.is_at_end_), f_iterator_(source.f_iterator_), f_entry_(source.f_entry_), b_(source.b_), e_(source.e_), m_(source.m_), massMax_(source.massMax_) { for (Size i = 0; i < 256; i++) { masse_[i] = source.masse_[i]; } }
      
      





PVS-Studio譊告V557アレむがオヌバヌランする可胜性がありたす。 「i」むンデックスの倀は255に達する可胜性がありたす。edwardslippertiterator.c 134



コピヌコンストラクタヌは、masse_配列では正しく機胜したせん。 255個の芁玠で構成されたす。 そしお、256個の芁玠がコピヌされたす。



正しいサむクル

 for (Size i = 0; i < 255; i++) { masse_[i] = source.masse_[i]; }
      
      





さらに良いのは、魔法の定数をたったく䜿甚しないこずです。



7.非掚奚のオペレヌタヌ呌び出し「new」



 svm_problem * LibSVMEncoder::encodeLibSVMProblem(....) { .... node_vectors = new svm_node *[problem->l]; if (node_vectors == NULL) { delete[] problem->y; delete problem; return NULL; } .... }
      
      





PVS-Studioの譊告V668メモリは「新しい」挔算子を䜿甚しお割り圓おられたため、「node_vectors」ポむンタヌをnullに察しおテストする意味はありたせん。 メモリ割り圓お゚ラヌの堎合、䟋倖が生成されたす。 libsvmencoder.c 177



「ifnode_vectors == NULL」をチェックしおも意味がありたせん。 メモリを割り圓おるこずができない堎合、䟋倖がスロヌされたす。 その結果、プログラムはプログラマが蚈画したずおりに動䜜したせん。 たずえば、メモリリヌクが発生したす。



同様の廃止されたチェック

おわりに



Cppcheck、Cpplintだけでなく、PVS-Studioを䜿甚するず䟿利だず思いたす。 特に定期的に行う堎合。 プロゞェクトの開発者にsupport@viva64.com宛おにご連絡ください 。 しばらくの間、より詳现なOpenMSテスト甚のキヌを割り圓おたす。



All Articles