ナニコヌンはマむクロワヌルドに興味を持ちたした

PVS-StudioおよびΌManagerマむクロマネヌゞャヌ

今回、小宇宙は私たちに゚ラヌの興味深い䟋を瀺したした。 PVS-Studioコヌドアナラむザヌを䜿甚しお、オヌプンなΌManagerプロゞェクトをテストしたした。 これは、顕埮鏡による自動むメヌゞング甚の゜フトりェアパッケヌゞです。



ÎŒManager



これは比范的小さなプロゞェクトです。 ゜ヌスコヌドの量は玄11メガバむトです。 このプロゞェクトが必芁な理由はわかりたせん。 私はそれを確認するように頌たれたした。 そしお今、ナニコヌンは助けを急いでいたす。 おそらく尋ねられた、必芁か぀有甚なプロゞェクト。



プロゞェクトサむト Micro-Manager



分析は、い぀ものように、 PVS-Studioアナラむザヌを䜿甚しお実行されたした。 ずころで、もし芋逃しおしたった堎合、朜圚的なナヌザヌが埅ち望んでいた比范を以䞋に瀺したす 。「 コヌドアナラむザヌの比范CppCat、Cppcheck、PVS-Studio、Visual Studio 」。



これで叙情的な䜙談は終わりです。 興味深いコヌドスニペットを芋おみたしょう。



long= int



long= int








ÎŒManagerプロゞェクトは、クロスプラットフォヌムであるず䞻匵しおいたす。 したがっお、「long」のタむプに泚意する必芁がありたす。 32ビットシステムでは、「long」型のサむズは「int」型のサむズず同じです。 ただし、64ビットシステムでは異なる堎合がありたす。 Win64では、タむプ「long」は32ビットのたたでした。 Linuxの64ビットの䞖界では 、「ロング」が64ビットである別のデヌタモデルが採甚されおいたす。 このタむプを䜿甚しお譊戒する必芁がありたす。



ÎŒManagerプロゞェクトには、次の倱敗したコヌドが含たれおいたす。

typedef struct _DCMOTSTATUS { unsigned short wChannel; // Channel ident. unsigned int lPosition; // Position in encoder counts. unsigned short wVelocity; // Velocity in encoder counts/sec. unsigned short wReserved; // Controller specific use unsigned int dwStatusBits; // Status bits (see #defines below). } DCMOTSTATUS; int MotorStage::ParseStatus(...., DCMOTSTATUS& stat) { .... memcpy(&stat.lPosition, buf + bufPtr, sizeof(long)); //<<<(1) bufPtr += sizeof(long); memcpy(&stat.wVelocity, buf + bufPtr, sizeof(unsigned short)); bufPtr += sizeof(unsigned short); memcpy(&stat.wReserved, buf + bufPtr, sizeof(unsigned short)); bufPtr += sizeof(unsigned short); memcpy(&stat.dwStatusBits, buf + bufPtr, sizeof(unsigned long)); //<<<(2) return DEVICE_OK; }
      
      





行1および2では、デヌタは 'int'型の倉数にコピヌされたす。 タむプ 'long'のサむズに等しいバむト数がコピヌされたす。 64ビットプログラムでは、 'long'は8バむトを占有できるこずを思い出しおください。 たた、タむプ 'int'は4バむトのみを取りたす。



1の堎合、心配するこずはありたせん。 構造䜓の次のメンバヌの倀を倉曎したす。 その埌、これらのメンバヌは再び満たされたす。 すでに正しい。



ただし、ケヌス2は重芁です。 最埌のメンバヌの倀が倉曎されたす。 構造の倖偎にレコヌドがありたす。 これがもたらすものは、運ず月の満ち欠けに䟝存したす。



PVS-Studio蚺断メッセヌゞのおかげで゚ラヌが怜出されたす。

ゎミ圧瞮機を停止したす



R2D2、ガベヌゞコンパクタヌ3263827を停止したす






 const unsigned char stopSgn[2] = {0x04, 0x66}; int MotorStage::Stop() { .... if (memcmp(stopSgn, answer, sizeof(stopSgn) != 0)) return ERR_UNRECOGNIZED_ANSWER; .... }
      
      





゚ラヌは、memcmp関数が1バむトのみを比范するこずです。 なんで 攻撃的な間違い。 閉じ括匧はそこに眮かれたせん。 比范するバむト数は次のように蚈算されたすsizeofstopSgn= 0.この匏は倀 'true'に等しく、1になりたす。



条件は次のようになりたす。

 if (memcmp(stopSgn, answer, sizeof(stopSgn)) != 0)
      
      





゚ラヌは蚺断によっお怜出されたしたV526察応するバッファヌが等しい堎合、「memcmp」関数は0を返したす。 状態の間違いを調べるこずを怜蚎しおください。 MotorStage.cpp 385



同䞀の比范



同䞀の比范






 const char* g_Out = "Out"; int FieldDiaphragm::OnCondensor(....) { .... std::string value; .... if (value == g_Out) return g_hub.SetCondensorPosition(*this, *GetCoreCallback(), 0); else if (value == g_Out) return g_hub.SetCondensorPosition(*this, *GetCoreCallback(), 1); .... }
      
      





2番目のifステヌトメントに無効な条件が含たれおいたす。 2番目の条件はどうあるべきか、わかりたせん。 ただし、2番目の条件が満たされるこずはないこずが明確にわかりたす。



゚ラヌ蚺断V517「ifA{...} else ifA{...}」パタヌンの䜿甚が怜出されたした。 論理゚ラヌが存圚する可胜性がありたす。 行を確認しおください1455、1457。LeicaDMR.cpp 1455



同様の゚ラヌを含む別のコヌドがありたす。 どうやら、いく぀かのホむヌルの䜍眮を蚭定する際に問題があるでしょう

 class Wheel : public CStateDeviceBase<Wheel> { .... unsigned wheelNumber_; .... }; int Wheel::SetWheelPosition(int position) { unsigned char cmd[4]; cmd[0] = moduleId_; cmd[2] = 0; cmd[3] = 58; if (wheelNumber_ == 1) { switch (position) { case 0: cmd[1] = 49; break; case 1: cmd[1] = 50; break; case 2: cmd[1] = 51; break; case 3: cmd[1] = 52; break; case 4: cmd[1] = 53; break; case 5: cmd[1] = 54; break; } } else if (wheelNumber_ == 1) { switch (position) { case 0: cmd[1] = 33; break; case 1: cmd[1] = 64; break; case 2: cmd[1] = 35; break; case 3: cmd[1] = 36; break; case 4: cmd[1] = 37; break; case 5: cmd[1] = 94; break; } .... }
      
      





PVS-Studio蚺断メッセヌゞV517「ifA{...} else ifA{...}」パタヌンの䜿甚が怜出されたした。 論理゚ラヌが存圚する可胜性がありたす。 行を確認しおください645、654。Ludl.cpp 645



私たちは䜕かを忘れたようです。



私たちは䜕かを芋た逃したたように感じたす








このコヌドを芋るこずをお勧めしたす。 䜕が欠けおいるのか気づきたしたか

 class MP285 { .... static int GetMotionMode() { return m_nMotionMode; } .... }; int ZStage::_SetPositionSteps(....) { .... if (MP285::GetMotionMode == 0) { long lOldZPosSteps = (long)MP285::Instance()->GetPositionZ(); dSec = (double)labs(lZPosSteps-lOldZPosSteps) / dVelocity; } else { dSec = (double)labs(lZPosSteps) / dVelocity; } .... }
      
      





非垞に重芁なこずがありたせん。 括匧は忘れられおいたす。 プログラムはGetMotionMode関数を呌び出し、返された倀をれロず比范する必芁がありたす。 代わりに、関数のアドレスがれロず比范されたす。



゚ラヌは蚺断によっお怜出されたしたV516奇劙な匏の怜査を怜蚎しおください。 NULL以倖の関数ポむンタヌはNULLず比范されたす 'MP285 :: GetMotionMode == 0'。 MP285ZStage.cpp 558



孀独な攟浪者



攟浪者






 int HalogenLamp::SetIntensity(long intensity) { .... command_stream.str().c_str(); .... }
      
      





これは䜕ですか リファクタリングの副䜜甚 䞍完党なコヌドですか 無害な䜙分なラむンや間違い



あなたはそのような孀独な攟浪者を芋るこずができる2぀の堎所がありたす

ブラフミン



ブラフミン






 int LeicaScopeInterface::GetDICTurretInfo(....) { .... std::string tmp; .... if (tmp == "DIC-TURRET") scopeModel_->dicTurret_.SetMotorized(true); else scopeModel_->dicTurret_.SetMotorized(true); .... }
      
      





これが゜フトりェアバラモンの倖芳です。 条件が満たされおいるかどうかに関係なく、同じコヌドが実行されたす。



譊告V523「then」ステヌトメントは「else」ステヌトメントず同等です。 LeicaDMIScopeInterface.cpp 1296



同様の皮類の別の間違い。 同じ行がここで比范されたす。 このコヌドにはおそらくタむプミスが含たれおいたす。

 int XLedDev::Initialize() { .... if (strcmp( XLed::Instance()->GetXLedStr(XLed::XL_WLedDevName + m_nLedDevNumber).c_str(), XLed::Instance()->GetXLedStr(XLed::XL_WLedDevName + m_nLedDevNumber).c_str() ) != 0) .... }
      
      





譊告V549 'strcmp'関数の最初の匕数は2番目の匕数ず同じです。 XLedDev.cpp 119



䜕かが合わない



䞍䞀臎








倀「false」および「true」は、タむプ「int」に暗黙的にキャストできたす。 たずえば、次のコヌドは正垞にコンパむルされたす。

 int F() { return false; }
      
      





関数Fは0を返したす。



時々ミスを犯し、タむプが「int」の゚ラヌステヌタスではなく、「false」たたは「true」を返したす。 これは物忘れによるものです。 ゚ラヌステヌタスが倀0で゚ンコヌドされおいおも問題ありたせん。



゚ラヌステヌタスがれロ以倖の倀で゚ンコヌドされおいる堎合、問題が発生したす。 これがたさにΌManagerプロゞェクトで発生するこずです。



次の定矩枈みの倀が利甚可胜です。

 #define DEVICE_OK 0 #define DEVICE_ERR 1 // generic, undefined error #define DEVICE_INVALID_PROPERTY 2 #define DEVICE_INVALID_PROPERTY_VALUE 3 #define DEVICE_INVALID_PROPERTY_TYPE 5 ....
      
      





0はすべおが正垞であるこずを意味するこずに泚意しおください。 その他の倀は、䜕らかの゚ラヌの存圚を瀺したす。



ÎŒManagerプロゞェクトコヌドには、ステヌタスず倀が「true」、「false」で混乱しおいるように思えたす。



CreateProperty関数を怜蚎しおください。

 int MM::PropertyCollection::CreateProperty(....) { if (Find(pszName)) return DEVICE_DUPLICATE_PROPERTY; .... if (!pProp->Set(pszValue)) return false; .... return DEVICE_OK; }
      
      





呌び出しpProp-> SetpszValueが倱敗した堎合、関数は「false」を返すこずに泚意しおください。 関数がステヌタスDEVICE_OKを返すこずがわかりたす。 これは非垞に奇劙です。



別の䞍審なコヌド

 int MM::PropertyCollection::RegisterAction( const char* pszName, MM::ActionFunctor* fpAct) { MM::Property* pProp = Find(pszName); if (!pProp) return DEVICE_INVALID_PROPERTY; pProp->RegisterAction(fpAct); return true; }
      
      





最埌に、「return true;」が衚瀺されたす。 これは、関数がステヌタスDEVICE_ERR 1䞀般的な未定矩の゚ラヌを返すこずを意味したす。 同時に、すべおが本圓に良いように思えたす。



おそらく、これらの堎所を疑わしいず呌ぶ理由を読んでいるのは奇劙に思えたすが、これらが間違いだずは蚀いたせん。 実際のずころ、特別な堎合を匷調するために「false」が䜿甚されおいるこずがありたす。 䟋

 int XYStage::Home() { .... if (ret != DEVICE_OK) { ostringstream os; os << "ReadFromComPort failed in " "XYStage::Busy, error code:" << ret; this->LogMessage(os.str().c_str(), false); return false; // Error, let's pretend all is fine } .... }
      
      





コメントに泚意しおください。 ゚ラヌが発生したした。 しかし、すべおが正垞で、れロを返すふりをしたす。 おそらく、このコヌドの特性を匷調するために、DEVICE_OKの代わりに「false」が曞き蟌たれたす。



そのようなコメントはほんの数䟋に圓おはたりたす。 しかし、他の堎所では、これが間違いなのか、「耳を䜿ったcなフェむント」なのかは明確ではありたせん。 私は、半分の堎所ですべおが正しいこずを提案し、半分は本圓に間違いであるず思いたす。



臭い








いずれにせよ、そのようなコヌドは非垞に悪臭がしたす。



すべおの䞍審な堎所のリストは次のずおりです。

奇劙な取埗



奇劙なな






 int pgFocus::GetOffset(double& offset) { MM_THREAD_GUARD_LOCK(&mutex); deviceInfo_.offset = offset; MM_THREAD_GUARD_UNLOCK(&mutex); return DEVICE_OK; }
      
      





それは私には思えたすか、このコヌドに䜕か問題がありたすか



アナラむザヌはこのコヌドを奜みたせんV669 'offset'匕数は非定数参照です。 アナラむザヌは、この匕数が倉曎されおいる䜍眮を刀別できたせん。 関数に゚ラヌが含たれおいる可胜性がありたす。 pgFocus.cpp 356



そしお本圓に、それは奇劙です。 この関数は「Get____」ず呌ばれたす。 関数はステヌタスを返したす。 圌女はたた、参照による匕数「オフセット」を取りたす。 そしお...そしお、それに䜕も曞きたせん。 どのように機胜するのかわかりたせん。 しかし、逆に割り圓おを行う必芁があったのでしょうか このようなもの

 offset = deviceInfo_.offset;
      
      





別の疑わしいGetTransmission関数

 int SpectralLMM5Interface::GetTransmission(...., double& transmission) { .... int16_t tr = 0; memcpy(&tr, answer + 1, 2); tr = ntohs(tr); transmission = tr/10; .... }
      
      





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



戻り倀はdouble型であるこずに泚意しおください送信に぀いお説明しおいたす。 しかし、この倀は奇劙に蚈算されたす。 敎数倀は10で陀算されたす。粟床が倱われるずいう匷い疑いがありたす。 たずえば、「tr」が5の堎合、陀算埌は0.5ではなく0になりたす。



おそらく正しいコヌドは次のようになりたす。

 transmission = tr/10.0;
      
      





゚ラヌたたぱラヌなし 第䞀印象は欺くこずができたす。



゚ラヌかどうか








C / C ++では、れロから始たる数字は8進圢匏で䞎えられたず芋なされたす。 ÎŒManagerプロゞェクトには、1぀の疑わしい堎所がありたす。

 int LeicaDMSTCHub::StopXY(MM::Device& device, MM::Core& core) { int ret = SetCommand(device, core, xyStage_, 010); if (ret != DEVICE_OK) return ret; return DEVICE_OK; }
      
      





PVS-Studio譊告V536䜿甚される定数倀は8進数圢匏で衚されるこずに泚意しおください。 10月010、12月8. LeicaDMSTCHub.cpp 142



圌らが本圓に8進数で曞かれた8番を䜿いたいのか、それずも間違いなのかは明らかではありたせん。 他の堎所では、10進数で曞かれた数倀がSetCommand関数に枡されたす。 たずえば、次のように

 int ret = SetCommand(device, core, xyStage_, 35, ack);
      
      





゚ラヌが芋぀かったかどうかはわかりたせんが、この堎所に぀いお蚀及する䟡倀はありたす。



完璧䞻矩者はinしおいる



完䞊列䞻矩者








重芁でない些现なこずがたくさんありたす。 ただし、ほずんどすべおのプログラマヌは完璧䞻矩者です。 ぀ぶやきたしょう。



䜙分な行がいっぱいです。 䞀䟋

 int XYStage::OnTriggerEndX(MM::PropertyBase* pProp, MM::ActionType eAct){ if (eAct == MM::BeforeGet) { int ret = GetCommandValue("trgse",xChannel_,chx_.trgse_); if (ret!=DEVICE_OK) if (ret!=DEVICE_OK) return ret; ..... }
      
      





2番目のチェックは明らかに䞍芁です。



別の䟋

 int AFC::Initialize() { int ret = DEVICE_OK; .... if (ret != DEVICE_OK) return ret; AddAllowedValue("DichroicMirrorIn", "0", 0); AddAllowedValue("DichroicMirrorIn", "1", 1); if (ret != DEVICE_OK) return ret; .... }
      
      





2番目のチェックも意味がありたせん。 それ以前は、倉数「ret」はどこでも倉曎されたせん。 2番目のテストは安党に削陀できたす。



このような远加のチェックがたくさんありたす。 リストを提䟛したす Micro-Manager-V571-V649.txt



ささいなこずから、sprintf関数を操䜜するずきに間違った圢匏に泚意するこずができたす。 笊号なし倉数は笊号付き倉数ずしお出力されたす。 これにより、倧きな倀が正しく印刷されない堎合がありたす。

 int MP285Ctrl::Initialize() { .... unsigned int nUm2UStepUnit = MP285::Instance()->GetUm2UStep(); .... sprintf(sUm2UStepUnit, "%d", nUm2UStepUnit); .... }
      
      





そのような堎所は3぀ありたした。

おわりに



これず他のプロゞェクトの単䞀のチェックは効果的ではありたせん。 この利点は、静的コヌドアナラむザヌを定期的に䜿甚する堎合にのみ埗られたす。 その埌、倚くの゚ラヌずタむプミスが早期に修正されたす。 コンパむラヌが生成する譊告の拡匵ずしお静的分析を怜蚎しおください。



Windowsオペレヌティングシステム甚の䞭芏暡および倧芏暡プロゞェクトを䜜成するチヌムの堎合、 PVS-Studio静的アナラむザヌの䜿甚をお勧めしたす。 料金は、チヌムの芏暡ず必芁なサポヌトレベルによっお異なりたす。



小さなチヌムや個々の開発者向けに、 CppCatツヌルを提䟛しおいたす。 個人ラむセンス-250ドル。 曎新-200ドル。 耇数のラむセンスを賌入する堎合-割匕。



Linuxを䜿甚しおいる堎合は、無料のCppcheckコヌドアナラむザヌに泚意するこずをお勧めしたす 。 たたは、PVS-Studioのスタンドアロンバヌゞョンをお詊しください。



PS



この蚘事の翻蚳「 ナニコヌンの小宇宙ぞの旅 」。



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




All Articles