Celestia: petualangan serangga di luar angkasa

Gambar 1

Celestia adalah simulator ruang tiga dimensi. Simulasi ruang memungkinkan kita menjelajahi alam semesta kita dalam tiga dimensi. Celestia tersedia di Windows, Linux, dan macOS. Proyek ini sangat kecil dan di dalamnya, menggunakan PVS-Studio, sejumlah kecil cacat terdeteksi. Namun, kami benar-benar ingin memperhatikannya, karena ini adalah proyek pendidikan populer yang berguna untuk ditingkatkan. Omong-omong, program ini digunakan dalam film populer, seri, dan program untuk mewakili ruang. Yang juga meningkatkan persyaratan kualitas kode.

Pendahuluan


Di situs web resmi proyek Celestia, Anda dapat menemukan deskripsi terperinci tentangnya. Kode sumber untuk proyek di-host di GitHub . Tidak termasuk perpustakaan dan tes, alat analisa memeriksa 166 file .cpp. Proyek ini kecil, tetapi cacat yang ditemukan cukup menarik.

Untuk menganalisis kode, kami menggunakan penganalisa kode statis PVS-Studio . Baik Celestia dan PVS-Studio adalah cross-platform. Untuk analisis, platform Windows dipilih. Proyek ini mudah dibangun dengan menarik dependensi menggunakan Vcpkg , manajer perpustakaan Microsoft. Menurut ulasan, itu lebih rendah dari kemampuan Conan , tetapi program ini juga nyaman digunakan.

Hasil analisis


Peringatan 1

V501 Ada sub-ekspresi yang identik ke kiri dan ke kanan operator '<': 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; } 

Betapa mudahnya membuat kesalahan saat menyalin kode. Kami menulis tentang ini di setiap ulasan. Rupanya, hanya analisis kode statis yang dapat membantu dalam situasi ini.

Pemrogram menyalin ekspresi bersyarat dan tidak sepenuhnya mengeditnya. Pilihan yang benar kemungkinan besar adalah ini:

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

Sebuah studi menarik tentang topik ini: " Evil hidup dalam fungsi perbandingan ."

Peringatan 2

V575 Fungsi 'memset' memproses elemen '0'. Periksa argumen ketiga. 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; .... } 

Penulis kode menggabungkan argumen kedua dan ketiga dengan fungsi memset . Alih-alih mengisi struktur dengan nol, ini ditunjukkan untuk mengisi 0 byte memori.

Peringatan 3

V595 Pointer 'tujuan' digunakan sebelum diverifikasi terhadap nullptr. Periksa baris: 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) { .... } .... } 

Pointer tujuan dereferenced dua baris lebih tinggi daripada dibandingkan dengan NULL . Kode semacam itu berpotensi menyebabkan kesalahan.

Peringatan 4

Kelas V702 harus selalu diturunkan dari std :: exception (dan yang sama) sebagai 'publik' (tidak ada kata kunci yang ditentukan, jadi kompiler secara default ke 'private'). 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 

Penganalisa mendeteksi kelas yang diwarisi dari kelas std :: exception melalui pengubah pribadi (ditentukan secara default). Warisan ini berbahaya karena dalam kasus warisan non-publik, ketika mencoba menangkap std :: exception exception , ia akan dilewati. Akibatnya, penangan pengecualian tidak berperilaku sebagaimana dimaksud.

Peringatan 5

V713 Pointer 's' digunakan dalam ekspresi logis sebelum diverifikasi terhadap nullptr dalam ekspresi logis yang sama. winmain.cpp 3031

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

Di satu tempat ekspresi kondisional, mereka lupa untuk merujuk pointer. Hasilnya adalah perbandingan pointer, bukan nilai di atasnya. Dan ini tidak masuk akal dalam situasi ini.

Peringatan 6

V773 Fungsi itu keluar tanpa melepaskan pointer 'vertexShader'. Kebocoran memori dimungkinkan. 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; } .... } 

Saat Anda keluar dari fungsi, memori dibebaskan oleh pointer glShader , tetapi tidak dihapus oleh pointer vertexShader .

Tempat yang sama lebih rendah dalam kode:

  • V773 Fungsi itu keluar tanpa melepaskan pointer 'fragmentShader'. Kebocoran memori dimungkinkan. modelviewwidget.cpp 1526

Peringatan 7

Ekspresi V547 '! InputFilename.empty ()' selalu benar. 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; } } .... } 

Periksa kembali nama file. Ini bukan kesalahan, tetapi karena fakta bahwa variabel inputFilename sudah diperiksa di awal fungsi, verifikasi dapat dihapus di bawah ini, yang akan membuat kode lebih kompak.

Peringatan 8

V556 Nilai dari berbagai tipe enum dibandingkan: switch (ENUM_TYPE_A) {case ENUM_TYPE_B: ...}. render.cpp 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; } .... } 

Dalam pernyataan beralih , nilai enumerasi bingung. Karena itu, enumerasi tipe yang berbeda dibandingkan di satu tempat: LabelVerticalAlignment dan AlignCenter .

Peringatan 9

V581 Ekspresi kondisional dari pernyataan 'jika' yang terletak berdampingan adalah identik. Periksa baris: 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); } .... } 

Penganalisa mendeteksi dua ekspresi kondisional yang identik dalam satu baris. Ada kesalahan atau dua kondisi dapat digabungkan menjadi satu, dan dengan demikian menyederhanakan kode.

Peringatan 10

V668 Tidak ada gunanya menguji pointer 'dp' terhadap null, karena memori dialokasikan menggunakan operator 'baru'. Pengecualian akan dihasilkan jika terjadi kesalahan alokasi memori. windatepicker.cpp 625

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

Nilai pointer yang dikembalikan oleh operator baru dibandingkan dengan nol. Jika operator tidak dapat mengalokasikan memori, maka sesuai dengan standar bahasa C ++, pengecualian std :: bad_alloc () dihasilkan. Jadi, memeriksa pointer ke kesetaraan ke nol tidak masuk akal.

Tiga cek serupa lainnya:

  • V668 Tidak ada gunanya menguji pointer 'mode' terhadap null, karena memori dialokasikan menggunakan operator 'baru'. Pengecualian akan dihasilkan jika terjadi kesalahan alokasi memori. winmain.cpp 2967
  • V668 Tidak ada gunanya menguji pointer 'dropTarget' terhadap null, karena memori dialokasikan menggunakan operator 'baru'. Pengecualian akan dihasilkan jika terjadi kesalahan alokasi memori. winmain.cpp 3272
  • V668 Tidak ada gunanya menguji pointer 'appCore' terhadap nol, karena memori dialokasikan menggunakan operator 'baru'. Pengecualian akan dihasilkan jika terjadi kesalahan alokasi memori. winmain.cpp 3352

Peringatan 11

V624 Konstanta 3.14159265 sedang digunakan. Nilai yang dihasilkan mungkin tidak akurat. Pertimbangkan untuk menggunakan konstanta M_PI dari <math.h>. 3dstocmod.cpp 62

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

Diagnostik direkomendasikan, tetapi lebih baik menggunakan konstanta siap pakai untuk nomor Pi dari perpustakaan standar.

Kesimpulan


Baru-baru ini, proyek ini telah dikembangkan oleh para penggemar, tetapi masih populer dan diminati dalam program pelatihan. Di Internet ada ribuan add-on dengan objek ruang angkasa yang berbeda. Celestia digunakan dalam The Day After Tomorrow dan seri dokumenter Through the Wormhole dengan Morgan Freeman .

Kami senang bahwa dengan memeriksa banyak proyek sumber terbuka, kami tidak hanya mempopulerkan metodologi analisis kode statis, tetapi juga berkontribusi pada pengembangan dunia proyek terbuka. Omong-omong, Anda juga dapat menggunakan penganalisa PVS-Studio tidak hanya untuk menguji proyek Anda sendiri, tetapi juga proyek pihak ketiga sebagai penggila. Untuk melakukan ini, Anda dapat menggunakan salah satu opsi untuk lisensi gratis.

Gunakan penganalisa kode statis, buat proyek Anda lebih andal dan lebih baik!



Jika Anda ingin berbagi artikel ini dengan audiens yang berbahasa Inggris, silakan gunakan tautan ke terjemahan: Svyatoslav Razmyslov. Celestia: Petualangan Bugs di Luar Angkasa .

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


All Articles