सेलेस्टिया: अंतरिक्ष में कीड़े का रोमांच

चित्र 1

सेलेस्टिया एक त्रि-आयामी अंतरिक्ष सिम्युलेटर है। अंतरिक्ष सिमुलेशन हमें तीन आयामों में हमारे ब्रह्मांड का पता लगाने की अनुमति देता है। सेलेस्टिया विंडोज, लिनक्स और मैकओएस पर उपलब्ध है। परियोजना बहुत छोटी है और इसमें पीवीएस-स्टूडियो का उपयोग करते हुए, बहुत कम संख्या में दोषों का पता लगाया जाता है। हालांकि, हम वास्तव में उस पर ध्यान देना चाहते हैं, क्योंकि यह एक लोकप्रिय शैक्षिक परियोजना है जो सुधार के लिए उपयोगी है। वैसे, कार्यक्रम का उपयोग लोकप्रिय फिल्मों, श्रृंखला और कार्यक्रमों में अंतरिक्ष का प्रतिनिधित्व करने के लिए किया जाता है। जो कोड की गुणवत्ता आवश्यकताओं को भी बढ़ाता है।

परिचय


सेलेस्टिया परियोजना की आधिकारिक वेबसाइट पर आप इसका विस्तृत विवरण पा सकते हैं। प्रोजेक्ट के लिए सोर्स कोड GitHub पर होस्ट किया गया है । पुस्तकालयों और परीक्षणों को छोड़कर, विश्लेषक ने 166 .cpp फ़ाइलों की जाँच की। परियोजना छोटी है, लेकिन इसमें पाए गए दोष काफी दिलचस्प हैं।

कोड का विश्लेषण करने के लिए, हमने PVS-Studio स्थिर कोड विश्लेषक का उपयोग किया। सेलेस्टिया और पीवीएस-स्टूडियो दोनों क्रॉस-प्लेटफॉर्म हैं। विश्लेषण के लिए, विंडोज प्लेटफॉर्म का चयन किया गया था। Microsoft लाइब्रेरी प्रबंधक Vcpkg का उपयोग करके निर्भरता को खींचकर परियोजना का निर्माण करना आसान था। समीक्षाओं के अनुसार, यह कॉनन की क्षमताओं से हीन है, लेकिन यह कार्यक्रम उपयोग करने के लिए सुविधाजनक भी था।

विश्लेषण के परिणाम


चेतावनी १

V501 बाईं और '<' ऑपरेटर के दाईं ओर समान उप-अभिव्यक्तियाँ हैं: b.nAttributes <b.nAttributes cmodfix.cpp 378

bool operator<(const Mesh::VertexDescription& a, const Mesh::VertexDescription& b) { if (a.stride < b.stride) return true; if (b.stride < a.stride) return false; if (a.nAttributes < b.nAttributes) // <= return true; if (b.nAttributes < b.nAttributes) // <= return false; for (uint32_t i = 0; i < a.nAttributes; i++) { if (a.attributes[i] < b.attributes[i]) return true; else if (b.attributes[i] < a.attributes[i]) return false; } return false; } 

कोड को कॉपी करते समय गलतियाँ करना कितना आसान है। हम हर समीक्षा में इस बारे में लिखते हैं। जाहिर है, केवल स्थैतिक कोड विश्लेषण इस स्थिति में मदद कर सकता है।

प्रोग्रामर ने सशर्त अभिव्यक्ति की प्रतिलिपि बनाई और इसे पूरी तरह से संपादित नहीं किया। सही विकल्प सबसे अधिक संभावना है:

 if (a.nAttributes < b.nAttributes) return true; if (b.nAttributes < a.nAttributes) return false; 

इस विषय पर एक दिलचस्प अध्ययन: " ईविल तुलना कार्यों में रहता है ।"

चेतावनी २

V575 'मेमसेट' फ़ंक्शन '0' तत्वों को प्रोसेस करता है। तीसरे तर्क का निरीक्षण करें। winmain.cpp 2235

 static void BuildScriptsMenu(HMENU menuBar, const fs::path& scriptsDir) { .... MENUITEMINFO info; memset(&info, sizeof(info), 0); info.cbSize = sizeof(info); info.fMask = MIIM_SUBMENU; .... } 

कोड के लेखक ने मेमसेट फ़ंक्शन के दूसरे और तीसरे तर्क को मिलाया । शून्य के साथ संरचना को भरने के बजाय, यह 0 बाइट्स मेमोरी को भरने के लिए संकेत दिया गया है।

चेतावनी ३

N5ptr के खिलाफ सत्यापित होने से पहले V595 'गंतव्यों' सूचक का उपयोग किया गया था। जाँच लाइनें: 48, 50. wintourguide.cpp 48

 BOOL APIENTRY TourGuideProc(....) { .... const DestinationList* destinations = guide->appCore->getDestinations(); Destination* dest = (*destinations)[0]; guide->selectedDest = dest; if (hwnd != NULL && destinations != NULL) { .... } .... } 

गंतव्यों के सूचक को NULL की तुलना में दो लाइनों से अधिक ऊंचा माना गया है । ऐसा कोड संभावित रूप से त्रुटि का कारण बन सकता है।

चेतावनी ४

V702 क्लासेस को हमेशा std :: अपवाद (और एक जैसे) से 'पब्लिक' के रूप में लिया जाना चाहिए (कोई कीवर्ड निर्दिष्ट नहीं किया गया था, इसलिए कंपाइलर इसे 'निजी' में डिफॉल्ट करता है)। fs.h 21

 class filesystem_error : std::system_error { public: filesystem_error(std::error_code ec, const char* msg) : std::system_error(ec, msg) { } }; // filesystem_error 

विश्लेषक ने पाया कि एक वर्ग std से विरासत में मिला है: निजी संशोधक (डिफ़ॉल्ट रूप से निर्दिष्ट) के माध्यम से अपवाद वर्ग। यह विरासत खतरनाक है क्योंकि गैर-सार्वजनिक विरासत के मामले में, जब std :: अपवाद अपवाद को पकड़ने की कोशिश की जाती है , तो इसे छोड़ दिया जाएगा। नतीजतन, अपवाद हैंडलर व्यवहार नहीं करते हैं।

चेतावनी ५

V713 सूचक की तार्किक अभिव्यक्ति में उपयोग किया गया था इससे पहले कि यह एक ही तार्किक अभिव्यक्ति में nullptr के खिलाफ सत्यापित किया गया था। winmain.cpp 3031

 static char* skipUntilQuote(char* s) { while (*s != '"' && s != '\0') s++; return s; } 

सशर्त अभिव्यक्ति के एक स्थान पर, वे पॉइंटर एस को रोकना भूल गए। परिणाम सूचक की तुलना था, उस पर मूल्य नहीं। और इस स्थिति में इसका कोई मतलब नहीं है।

चेतावनी ६

V773 'वर्टेक्सशेयर' पॉइंटर को जारी किए बिना फ़ंक्शन को बाहर किया गया था। एक स्मृति रिसाव संभव है। modelviewwidget.cpp 1517

 GLShaderProgram* ModelViewWidget::createShader(const ShaderKey& shaderKey) { .... auto* glShader = new GLShaderProgram(); auto* vertexShader = new GLVertexShader(); if (!vertexShader->compile(vertexShaderSource.toStdString())) { qWarning("Vertex shader error: %s", vertexShader->log().c_str()); std::cerr << vertexShaderSource.toStdString() << std::endl; delete glShader; return nullptr; } .... } 

जब आप फ़ंक्शन से बाहर निकलते हैं, तो मेमोरी को ग्लेशियर पॉइंटर द्वारा मुक्त किया जाता है, लेकिन यह वर्टेक्शैडर पॉइंटर द्वारा साफ़ नहीं किया जाता है।

एक ही जगह कोड में कम है:

  • V773 'टुकड़ा' पॉइंटर को जारी किए बिना फ़ंक्शन को बाहर किया गया था। एक स्मृति रिसाव संभव है। modelviewwidget.cpp 1526

चेतावनी 7

V547 अभिव्यक्ति '! InputFilename.empty ()' हमेशा सच होता है। makexindex.cpp 128

 int main(int argc, char* argv[]) { if (!parseCommandLine(argc, argv) || inputFilename.empty()) { Usage(); return 1; } istream* inputFile = &cin; if (!inputFilename.empty()) { inputFile = new ifstream(inputFilename, ios::in); if (!inputFile->good()) { cerr << "Error opening input file " << inputFilename << '\n'; return 1; } } .... } 

फ़ाइल का नाम फिर से जाँचें। यह एक त्रुटि नहीं है, लेकिन इस तथ्य के कारण कि फ़ंक्शन की शुरुआत में पहले से ही इनपुटफिल्म चर की जांच की जाती है, सत्यापन को नीचे हटाया जा सकता है, जो कोड को अधिक कॉम्पैक्ट बना देगा।

चेतावनी 8

V556 विभिन्न एनम प्रकारों के मूल्यों की तुलना की जाती है: स्विच (ENUM_TYPE_A) {मामला ENUM_TYPE_B: ...}। प्रस्तुत करें। 7457 टीपी

 enum LabelAlignment { AlignCenter, AlignLeft, AlignRight }; enum LabelVerticalAlignment { VerticalAlignCenter, VerticalAlignBottom, VerticalAlignTop, }; struct Annotation { .... LabelVerticalAlignment valign : 3; .... }; void Renderer::renderAnnotations(....) { .... switch (annotations[i].valign) { case AlignCenter: vOffset = -font[fs]->getHeight() / 2; break; case VerticalAlignTop: vOffset = -font[fs]->getHeight(); break; case VerticalAlignBottom: vOffset = 0; break; } .... } 

स्विच स्टेटमेंट में , एन्यूमरेशन वैल्यूज़ उलझन में हैं। इसके कारण, विभिन्न प्रकारों की गणना की एक ही स्थान पर तुलना की जाती है: LabelVerticalAlignment और AlignCenter

चेतावनी ९

V581 एक-दूसरे के साथ स्थित 'if' स्टेटमेंट्स के सशर्त भाव समान हैं। चेक लाइनें: 2844, 2850. shadermanager.cpp 2850

 GLVertexShader* ShaderManager::buildParticleVertexShader(const ShaderProperties& props) { .... if (props.texUsage & ShaderProperties::PointSprite) { source << "uniform float pointScale;\n"; source << "attribute float pointSize;\n"; } if (props.texUsage & ShaderProperties::PointSprite) { source << DeclareVarying("pointFade", Shader_Float); } .... } 

विश्लेषक ने एक पंक्ति में दो समान सशर्त अभिव्यक्तियों का पता लगाया। या तो एक गलती है या दो स्थितियों को एक में जोड़ा जा सकता है, और इस तरह कोड को सरल बनाया जा सकता है।

चेतावनी १०

V668 नल के खिलाफ 'dp' पॉइंटर के परीक्षण में कोई समझदारी नहीं है, क्योंकि स्मृति को 'नए' ऑपरेटर का उपयोग करके आवंटित किया गया था। मेमोरी आवंटन त्रुटि के मामले में अपवाद उत्पन्न होगा। windatepicker.cpp 625

 static LRESULT DatePickerCreate(HWND hwnd, CREATESTRUCT& cs) { DatePicker* dp = new DatePicker(hwnd, cs); if (dp == NULL) return -1; .... } 

नए ऑपरेटर द्वारा लौटाए गए पॉइंटर का मूल्य शून्य की तुलना में है। यदि ऑपरेटर मेमोरी को आवंटित नहीं कर सकता है, तो C ++ भाषा मानक के अनुसार, एक अपवाद std :: bad_alloc () फेंक दिया जाता है । इस प्रकार, सूचक को शून्य की समानता की जांच करने से कोई मतलब नहीं है।

तीन और समान जांच:

  • V668 नल के खिलाफ 'मोड्स' पॉइंटर के परीक्षण में कोई समझदारी नहीं है, क्योंकि स्मृति को 'नए' ऑपरेटर का उपयोग करके आवंटित किया गया था। मेमोरी आवंटन त्रुटि के मामले में अपवाद उत्पन्न होगा। winmain.cpp 2967
  • V668 नल के खिलाफ 'ड्रॉपटार्ग' पॉइंटर के परीक्षण में कोई समझदारी नहीं है, क्योंकि स्मृति को 'नए' ऑपरेटर के उपयोग से आवंटित किया गया था। मेमोरी आवंटन त्रुटि के मामले में अपवाद उत्पन्न होगा। winmain.cpp 3272
  • V668 नल के खिलाफ 'appCore' पॉइंटर के परीक्षण में कोई समझदारी नहीं है, क्योंकि स्मृति को 'नए' ऑपरेटर का उपयोग करके आवंटित किया गया था। मेमोरी आवंटन त्रुटि के मामले में अपवाद उत्पन्न होगा। winmain.cpp 3352

चेतावनी ११

V624 निरंतर 3.14159265 का उपयोग किया जा रहा है। परिणामी मूल्य गलत हो सकता है। <Math.h> से M_PI निरंतर का उपयोग करने पर विचार करें। 3dstocmod.cpp 62

 int main(int argc, char* argv[]) { .... Model* newModel = GenerateModelNormals(*model, float(smoothAngle * 3.14159265 / 180.0), weldVertices, weldTolerance); .... } 

डायग्नोस्टिक्स की सिफारिश की जाती है, लेकिन मानक पुस्तकालय से पाई नंबर के लिए तैयार स्थिरांक का उपयोग करना वास्तव में बेहतर है।

निष्कर्ष


हाल ही में, परियोजना उत्साही लोगों द्वारा विकसित की गई है, लेकिन अभी भी लोकप्रिय है और प्रशिक्षण कार्यक्रमों में मांग में है। इंटरनेट पर विभिन्न अंतरिक्ष वस्तुओं के साथ हजारों ऐड-ऑन हैं। सेलेस्टिया का उपयोग द डे आफ्टर टुमॉरो और मॉर्गन फ्रीमैन के साथ डॉक्यूमेंट्री सीरीज़ थ्रू द वर्महोल में किया गया था

हमें खुशी है कि कई ओपन सोर्स प्रोजेक्ट्स का परीक्षण करके, हम न केवल स्टैटिक कोड विश्लेषण की कार्यप्रणाली को लोकप्रिय बनाते हैं, बल्कि खुली परियोजनाओं की दुनिया के विकास में भी योगदान देते हैं। वैसे, आप पीवीएस-स्टूडियो विश्लेषक का उपयोग न केवल अपने स्वयं के परीक्षण के लिए कर सकते हैं, बल्कि एक उत्साही के रूप में तीसरे पक्ष के प्रोजेक्ट भी कर सकते हैं। ऐसा करने के लिए, आप नि: शुल्क लाइसेंस के लिए विकल्पों में से एक का उपयोग कर सकते हैं।

स्थिर कोड एनालाइज़र का उपयोग करें, अपनी परियोजनाओं को अधिक विश्वसनीय और बेहतर बनाएं!



यदि आप इस लेख को अंग्रेजी बोलने वाले दर्शकों के साथ साझा करना चाहते हैं, तो कृपया अनुवाद के लिंक का उपयोग करें: Svyatoslav Razmyslov। सेलेस्टिया: स्पेस में बग्स एडवेंचर्स

Source: https://habr.com/ru/post/hi469743/


All Articles