PVS-Studio dan Integrasi Berkelanjutan: TeamCity. Buka Analisis Proyek RollerCoaster Tycoon 2



Salah satu skenario paling relevan untuk menggunakan penganalisis PVS-Studio adalah integrasinya dengan sistem CI. Dan meskipun analisis proyek PVS-Studio dari hampir semua sistem integrasi berkelanjutan dapat dibuat hanya dalam beberapa perintah, kami terus membuat proses ini semakin nyaman. PVS-Studio sekarang mendukung konversi keluaran penganalisis ke format untuk TeamCity - TeamCity Inspections Type. Mari kita lihat cara kerjanya.



Informasi tentang perangkat lunak yang digunakan



PVS-Studio adalah penganalisis statis dari kode , ++, C # dan Java yang dirancang untuk memfasilitasi tugas menemukan dan memperbaiki berbagai jenis kesalahan. Penganalisis dapat digunakan di Windows, Linux, dan macOS. Dalam artikel ini kami akan secara aktif menggunakan tidak hanya penganalisis itu sendiri, tetapi juga beberapa utilitas dari kit distribusinya.



CLMonitor adalah server pemantauan yang memantau peluncuran kompiler. Ini harus dijalankan segera sebelum mulai membangun proyek Anda. Dalam mode monitor, server akan menghalangi jalannya semua kompiler yang didukung. Perlu dicatat bahwa utilitas ini hanya dapat digunakan untuk menganalisis proyek C / C ++.



PlogConverter adalah utilitas untuk mengubah laporan penganalisis ke dalam format yang berbeda.



Informasi tentang proyek yang diinvestigasi



Mari kita coba fungsionalitas ini dengan contoh praktis - mari kita analisis proyek OpenRCT2.



OpenRCT2 adalah implementasi open source dari RollerCoaster Tycoon 2 (RCT2), memperluasnya dengan fitur baru dan perbaikan bug. Gameplay berputar di sekitar pembangunan dan pemeliharaan taman hiburan, yang berisi atraksi, toko, dan fasilitas. Pemain harus mencoba menghasilkan keuntungan dan mempertahankan reputasi yang baik untuk taman sambil membuat para tamu senang. OpenRCT2 memungkinkan Anda bermain di skenario dan kotak pasir. Skenario mengharuskan pemain menyelesaikan tugas tertentu pada waktu yang ditentukan, sedangkan kotak pasir memungkinkan pemain untuk membangun taman yang lebih fleksibel tanpa batasan atau keuangan apa pun.



Pengaturan



Untuk menghemat waktu, saya mungkin akan melewatkan proses instalasi dan mulai dari saat server TeamCity berjalan di komputer saya. Kita perlu pergi ke: localhost: {port yang ditentukan selama instalasi} (dalam kasus saya, localhost: 9090) dan memasukkan data otorisasi. Setelah masuk kita akan bertemu dengan:



image3.png


Klik pada tombol Buat Proyek. Selanjutnya, pilih Secara Manual, isi bidang.



image5.png


Setelah mengklik tombol Buat , kita disambut oleh jendela dengan pengaturan.



image7.png


Klik Buat konfigurasi build .



image9.png


Isi kolom, klik Buat . Kami melihat jendela menawarkan untuk memilih sistem kontrol versi. Karena sumber sudah berada secara lokal, klik Lewati .



image11.png


Akhirnya, kami beralih ke pengaturan proyek.



image13.png


Tambahkan langkah build dengan mengklik: Langkah build -> Tambahkan langkah build .



image15.png


Di sini kami memilih:



  • Jenis pelari -> Baris Perintah
  • Jalankan -> Skrip Kustom


Karena kami akan menganalisis selama kompilasi proyek, perakitan dan analisis harus menjadi satu langkah, jadi isi bidang Skrip Kustom :



image17.png


Kami akan membahas langkah-langkah individu nanti. Penting bahwa memuat penganalisis, membangun proyek, menganalisisnya, mengeluarkan laporan dan memformat semuanya hanya membutuhkan sebelas baris kode.



Hal terakhir yang perlu kita lakukan adalah mengatur variabel lingkungan, yang telah saya tunjukkan dalam beberapa cara untuk meningkatkan keterbacaannya. Untuk melakukan ini, buka: Parameter -> Tambahkan parameter baru dan tambahkan tiga variabel:



image19.png


Tetap mengklik tombol Run di sudut kanan atas. Sementara proyek sedang dirakit dan dianalisis, saya akan memberi tahu Anda tentang skripnya.



Skrip langsung



Pertama, kita perlu mengunduh distribusi PVS-Studio terbaru. Untuk ini kami menggunakan pengelola paket Cokelat. Bagi yang ingin tahu lebih banyak tentang ini, ada artikel terkait :



choco install pvs-studio -y


Selanjutnya, mari kita luncurkan utilitas pelacakan perakitan proyek CLMonitor.



%CLmon% monitor –-attach


Kemudian kita akan membangun proyek, jalur ke versi MSBuild yang perlu saya bangun digunakan sebagai variabel lingkungan MSB



%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable


Mari masukkan login PVS-Studio dan kunci lisensi:



%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%


Setelah build selesai, jalankan CLMonitor lagi untuk menghasilkan file yang telah diproses sebelumnya dan analisis statis:



%CLmon% analyze -l "c:\ptest.plog"


Kemudian kami akan menggunakan satu utilitas lagi dari kit distribusi kami. PlogConverter mengubah laporan dari standar ke format khusus TeamCity. Berkat ini, kami dapat melihatnya langsung di jendela perakitan.



%PlogConverter% "c:\ptest.plog" --renderTypes=TeamCity -o "C:\temp"


Langkah terakhir adalah mengeluarkan laporan yang diformat ke stdout , yang akan diambil oleh pengurai TeamCity.



type "C:\temp\ptest.plog_TeamCity.txt"


Kode skrip lengkap:



choco install pvs-studio -y
%CLmon% monitor --attach
set platform=x64
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
%CLmon% analyze -l "c:\ptest.plog"
%PlogConverter% "c:\ptest.plog" --renderTypes=TeamCity -o "C:\temp"
type "C:\temp\ptest.plog_TeamCity.txt"


Sementara itu, perakitan dan analisis proyek telah berhasil diselesaikan, kita dapat pergi ke tab Proyek dan memverifikasi ini.



image21.png


Sekarang klik Total Inspeksi untuk melihat laporan penganalisis:



image23.png


Peringatan dikelompokkan berdasarkan nomor aturan diagnostik. Untuk menavigasi kode, Anda perlu mengklik nomor baris dengan peringatan. Mengklik tanda tanya di sudut kanan atas akan membuka tab dokumentasi baru untuk Anda. Anda juga dapat menavigasi kode dengan mengklik nomor baris peringatan penganalisis. Navigasi dari komputer jarak jauh dimungkinkan menggunakan penanda SourceTreeRoot . Mereka yang tertarik dengan mode operasi penganalisis ini dapat membiasakan diri dengan bagian dokumentasi yang sesuai .



Melihat Hasil Analyzer



Setelah kami selesai menerapkan dan mengonfigurasi build, saya sarankan untuk melihat beberapa peringatan menarik yang ditemukan dalam proyek yang sedang dipelajari.



Peringatan N1



V773 [CWE-401] Pengecualian dilemparkan tanpa melepaskan penunjuk 'hasil'. Kebocoran memori mungkin terjadi. libopenrct2 ObjectFactory.cpp 443



Object* CreateObjectFromJson(....)
{
  Object* result = nullptr;
  ....
  result = CreateObject(entry);
  ....
  if (readContext.WasError())
  {
    throw std::runtime_error("Object has errors");
  }
  ....
}

Object* CreateObject(const rct_object_entry& entry)
{
  Object* result;
  switch (entry.GetType())
  {
    case OBJECT_TYPE_RIDE:
      result = new RideObject(entry);
      break;
    case OBJECT_TYPE_SMALL_SCENERY:
      result = new SmallSceneryObject(entry);
      break;
    case OBJECT_TYPE_LARGE_SCENERY:
      result = new LargeSceneryObject(entry);
      break;
    ....
    default:
      throw std::runtime_error("Invalid object type");
  }
  return result;
}


Penganalisis melihat kesalahan bahwa setelah alokasi memori dinamis di CreateObject , ketika pengecualian dilemparkan, memori tidak dihapus; oleh karena itu, terjadi kebocoran memori.



Peringatan N2



V501 Ada sub-ekspresi identik '(1ULL << WIDX_MONTH_BOX)' di kiri dan kanan '|' operator. libopenrct2ui Cheats.cpp 487



static uint64_t window_cheats_page_enabled_widgets[] = 
{
  MAIN_CHEAT_ENABLED_WIDGETS |
  (1ULL << WIDX_NO_MONEY) |
  (1ULL << WIDX_ADD_SET_MONEY_GROUP) |
  (1ULL << WIDX_MONEY_SPINNER) |
  (1ULL << WIDX_MONEY_SPINNER_INCREMENT) |
  (1ULL << WIDX_MONEY_SPINNER_DECREMENT) |
  (1ULL << WIDX_ADD_MONEY) |
  (1ULL << WIDX_SET_MONEY) |
  (1ULL << WIDX_CLEAR_LOAN) |
  (1ULL << WIDX_DATE_SET) |
  (1ULL << WIDX_MONTH_BOX) |  // <=
  (1ULL << WIDX_MONTH_UP) |
  (1ULL << WIDX_MONTH_DOWN) |
  (1ULL << WIDX_YEAR_BOX) |
  (1ULL << WIDX_YEAR_UP) |
  (1ULL << WIDX_YEAR_DOWN) |
  (1ULL << WIDX_DAY_BOX) |
  (1ULL << WIDX_DAY_UP) |
  (1ULL << WIDX_DAY_DOWN) |
  (1ULL << WIDX_MONTH_BOX) |  // <=
  (1ULL << WIDX_DATE_GROUP) |
  (1ULL << WIDX_DATE_RESET),
  ....
};


Hanya sedikit orang, kecuali penganalisis statis, yang dapat lulus tes perhatian ini. Contoh salin-tempel ini tepat untuk itu.



Peringatan N3



V703 Aneh bahwa bidang 'bendera' di kelas turunan 'RCT12BannerElement' menimpa bidang di kelas dasar 'RCT12TileElementBase'. Periksa jalur: RCT12.h: 570, RCT12.h: 259. libopenrct2 RCT12.h 570



struct RCT12SpriteBase
{
  ....
  uint8_t flags;
  ....
};
struct rct1_peep : RCT12SpriteBase
{
  ....
  uint8_t flags;
  ....
};


Tentu saja, menggunakan variabel dengan nama yang sama di kelas dasar dan pewaris tidak selalu merupakan kesalahan. Namun, teknologi pewarisan itu sendiri mengasumsikan keberadaan semua bidang kelas induk pada anak. Dengan mendeklarasikan bidang dengan nama yang sama di pewaris, kami menimbulkan kebingungan.



Peringatan N4



V793 Aneh bahwa hasil dari pernyataan 'imageDirection / 8' adalah bagian dari kondisi. Mungkin pernyataan ini seharusnya dibandingkan dengan hal lain. libopenrct2 ObservationTower.cpp 38



void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
  if ((imageDirection / 8) && (imageDirection / 8) != 3)
  {
    ....
  }
  ....
}


Mari kita lihat lebih dekat. The imageDirection / 8 ekspresi akan false jika imageDirection adalah dalam kisaran -7 ke 7. Bagian kedua: ! (ImageDirection / 8) = 3 cek yang imageDirection untuk keluar dari kisaran: -31 ke -24 dan 24-31 masing-masing. Tampaknya agak aneh bagi saya untuk memeriksa angka untuk memasukkan kisaran tertentu dengan cara ini, dan bahkan jika tidak ada kesalahan dalam fragmen kode ini, saya akan merekomendasikan penulisan ulang kondisi ini agar lebih eksplisit. Ini akan membuat hidup lebih mudah bagi orang-orang yang akan membaca dan memelihara kode ini.



Peringatan N5



V587Urutan ganjil penugasan semacam ini: A = B; B = A;. Periksa baris: 1115, 1118.libopenrct2ui MouseInput.cpp 1118



void process_mouse_over(....)
{
  ....
  switch (window->widgets[widgetId].type)
  {
    case WWT_VIEWPORT:
      ebx = 0;
      edi = cursorId;                                 // <=
      // Window event WE_UNKNOWN_0E was called here,
      // but no windows actually implemented a handler and
      // it's not known what it was for
      cursorId = edi;                                 // <=
      if ((ebx & 0xFF) != 0)
      {
        set_cursor(cursorId);
        return;
      }
      break;
      ....
  }
  ....
}


Potongan kode ini kemungkinan besar diperoleh dengan dekompilasi. Kemudian, dilihat dari komentar yang ditinggalkan, beberapa kode yang tidak berfungsi telah dihapus. Namun, beberapa operasi pada cursorId tetap ada , yang juga tidak masuk akal.



N6 Peringatan



V1004 [CWE-476] Penunjuk 'pemain' digunakan dengan tidak aman setelah diverifikasi terhadap nullptr. Jalur cek: 2085, 2094.libopenrct2 Network.cpp 2094



void Network::ProcessPlayerList()
{
  ....
  auto* player = GetPlayerByID(pendingPlayer.Id);
  if (player == nullptr)
  {
    // Add new player.
    player = AddPlayer("", "");
    if (player)                                          // <=
    {
      *player = pendingPlayer;
       if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER)
       {
         _serverConnection->Player = player;
       }
    }
    newPlayers.push_back(player->Id);                    // <=
  }
  ....
}


Kode ini cukup sederhana untuk diperbaiki, Anda perlu memeriksa pemutar untuk penunjuk null untuk ketiga kalinya , atau menambahkannya ke badan operator bersyarat. Saya menyarankan opsi kedua:



void Network::ProcessPlayerList()
{
  ....
  auto* player = GetPlayerByID(pendingPlayer.Id);
  if (player == nullptr)
  {
    // Add new player.
    player = AddPlayer("", "");
    if (player)
    {
      *player = pendingPlayer;
      if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER)
      {
        _serverConnection->Player = player;
      }
      newPlayers.push_back(player->Id);
    }
  }
  ....
}


N7 Peringatan



V547 [CWE-570] Ekspresi 'name == nullptr' selalu salah. libopenrct2 ServerList.cpp 102



std::optional<ServerListEntry> ServerListEntry::FromJson(...)
{
  auto name = json_object_get(server, "name");
  .....
  if (name == nullptr || version == nullptr)
  {
    ....
  }
  else
  {
    ....
    entry.name = (name == nullptr ? "" : json_string_value(name));
    ....
  }
  ....
}


Anda dapat menyingkirkan baris kode yang sulit dibaca dalam satu gerakan dan menyelesaikan masalah dengan memeriksa nullptr . Saya sarankan untuk mengubah kode sebagai berikut:



std::optional<ServerListEntry> ServerListEntry::FromJson(...)
{
  auto name = json_object_get(server, "name");
  .....
  if (name == nullptr || version == nullptr)
  {
    name = ""
    ....
  }
  else
  {
    ....
    entry.name = json_string_value(name);
    ....
  }
  ....
}


Peringatan N8



V1048 [CWE-1164] Variabel 'ColumnHeaderPressedCurrentState' diberi nilai yang sama. libopenrct2ui CustomListView.cpp 510



void CustomListView::MouseUp(....)
{
  ....
  if (!ColumnHeaderPressedCurrentState)
  {
    ColumnHeaderPressed = std::nullopt;
    ColumnHeaderPressedCurrentState = false;
    Invalidate();
  }
}


Kode tersebut terlihat sangat aneh. Saya pikir ada kesalahan ketik atau menjadi subjek, atau ketika menetapkan ulang nilai variabel ColumnHeaderPressedCurrentState ke false .



Keluaran



Seperti yang bisa kita lihat, mengintegrasikan penganalisis statis PVS-Studio ke dalam proyek TeamCity Anda cukup sederhana. Yang perlu Anda lakukan hanyalah menulis satu file konfigurasi kecil. Peninjauan kode akan memungkinkan Anda mengidentifikasi masalah segera setelah pembuatan, yang akan membantu menghilangkannya saat kerumitan dan biaya pengeditan masih kecil.





Jika Anda ingin berbagi artikel ini dengan audiens berbahasa Inggris, silakan gunakan tautan terjemahan: Vladislav Stolyarov. PVS-Studio dan Integrasi Berkelanjutan: TeamCity. Analisis proyek Open RollerCoaster Tycoon 2 .



All Articles