PVS-Studio dan Integrasi Berkelanjutan: TeamCity. Analisis proyek Open RollerCoaster Tycoon 2

PVS-Studio dan Integrasi Berkelanjutan: TeamCity. Analisis proyek Open RollerCoaster Tycoon 2
Salah satu skenario terkini dalam penggunaan penganalisis PVS-Studio adalah integrasinya dengan sistem CI. Dan meskipun analisis proyek PVS-Studio dari hampir semua sistem integrasi berkelanjutan dapat dibangun hanya dalam beberapa perintah, kami terus membuat proses ini menjadi lebih nyaman. PVS-Studio sekarang memiliki dukungan untuk mengubah keluaran penganalisis ke dalam format untuk TeamCity - Jenis Inspeksi TeamCity. Mari kita lihat cara kerjanya.

Informasi tentang perangkat lunak yang digunakan

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

Monitor CLM β€” adalah server pemantauan yang memantau peluncuran kompiler. Itu harus dijalankan segera sebelum mulai membangun proyek Anda. Dalam mode pengintaian, server akan mencegat proses semua kompiler yang didukung. Perlu dicatat bahwa utilitas ini hanya dapat digunakan untuk menganalisis proyek C/C++.

Konverter Plog – utilitas untuk mengonversi laporan penganalisis ke dalam format berbeda.

Informasi tentang proyek yang sedang dipelajari

Mari kita coba fungsi ini pada contoh praktis - mari kita menganalisis proyek OpenRCT2.

BukaRCT2 - implementasi terbuka dari game RollerCoaster Tycoon 2 (RCT2), memperluasnya dengan fungsi baru dan memperbaiki bug. Gameplaynya berkisar pada membangun dan memelihara taman hiburan yang berisi wahana, toko, dan fasilitas. Pemain harus berusaha mendapatkan keuntungan dan menjaga reputasi baik taman sekaligus membuat para tamu senang. OpenRCT2 memungkinkan Anda bermain dalam skenario dan kotak pasir. Skenario mengharuskan pemain untuk menyelesaikan tugas tertentu dalam waktu yang ditentukan, sementara Sandbox memungkinkan pemain membangun taman yang lebih fleksibel tanpa batasan atau keuangan apa pun.

pengaturan

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

PVS-Studio dan Integrasi Berkelanjutan: TeamCity. Analisis proyek Open RollerCoaster Tycoon 2
Klik pada tombol Buat Proyek. Selanjutnya, pilih Secara Manual dan isi kolomnya.

PVS-Studio dan Integrasi Berkelanjutan: TeamCity. Analisis proyek Open RollerCoaster Tycoon 2
Setelah menekan tombol membuat, kita akan disambut oleh jendela dengan pengaturan.

PVS-Studio dan Integrasi Berkelanjutan: TeamCity. Analisis proyek Open RollerCoaster Tycoon 2
Ayo klik Buat konfigurasi build.

PVS-Studio dan Integrasi Berkelanjutan: TeamCity. Analisis proyek Open RollerCoaster Tycoon 2
Isi kolomnya dan klik membuat. Kami melihat jendela yang meminta Anda untuk memilih sistem kontrol versi. Karena sumber sudah berlokasi secara lokal, klik Melewatkan.

PVS-Studio dan Integrasi Berkelanjutan: TeamCity. Analisis proyek Open RollerCoaster Tycoon 2
Terakhir, kami beralih ke pengaturan proyek.

PVS-Studio dan Integrasi Berkelanjutan: TeamCity. Analisis proyek Open RollerCoaster Tycoon 2
Mari tambahkan langkah perakitan, untuk melakukan ini klik: Langkah pembuatan -> Tambahkan langkah pembuatan.

PVS-Studio dan Integrasi Berkelanjutan: TeamCity. Analisis proyek Open RollerCoaster Tycoon 2
Di sini kita memilih:

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

Karena kita akan melakukan analisis selama kompilasi proyek, perakitan dan analisis harus menjadi satu langkah, jadi isilah kolom tersebut Skrip Khusus:

PVS-Studio dan Integrasi Berkelanjutan: TeamCity. Analisis proyek Open RollerCoaster Tycoon 2
Kami akan melihat langkah-langkah individualnya nanti. Penting untuk memuat penganalisis, merakit proyek, menganalisisnya, mengeluarkan laporan, dan memformat hanya membutuhkan sebelas baris kode.

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

PVS-Studio dan Integrasi Berkelanjutan: TeamCity. Analisis proyek Open RollerCoaster Tycoon 2
Yang harus Anda lakukan hanyalah menekan tombol Run di sudut kanan atas. Saat proyek sedang disusun dan dianalisis, saya akan memberi tahu Anda tentang naskahnya.

Skenario langsung

Pertama, kita perlu mendownload distribusi PVS-Studio terbaru. Untuk ini kami menggunakan manajer paket Chocolatey. Bagi yang ingin tahu lebih banyak tentang ini, ada yang sesuai artikel:

choco install pvs-studio -y

Selanjutnya, mari kita luncurkan utilitas pelacakan pembangunan proyek CLMonitor.

%CLmon% monitor –-attach

Kemudian kita akan membangun proyek sebagai variabel lingkungan MSB adalah jalur ke versi MSBuild yang perlu saya buat

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

Mari masukkan login dan kunci lisensi untuk PVS-Studio:

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

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

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

Kemudian kami akan menggunakan utilitas lain dari distribusi kami. PlogConverter mengonversi laporan dari format standar ke format khusus TeamCity. Berkat ini, kami dapat melihatnya langsung di jendela build.

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

Langkah terakhir adalah menampilkan laporan yang telah diformat stdout, yang akan diambil oleh parser TeamCity.

type "C:tempptest.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:tempptest.plog_TeamCity.txt"

Sementara itu, perakitan dan analisis proyek telah berhasil diselesaikan, kita dapat membuka tab Proyek Π΅ ΠΎΠΌ.

PVS-Studio dan Integrasi Berkelanjutan: TeamCity. Analisis proyek Open RollerCoaster Tycoon 2
Sekarang mari kita klik Jumlah Inspeksiuntuk melihat laporan penganalisis:

PVS-Studio dan Integrasi Berkelanjutan: TeamCity. Analisis proyek Open RollerCoaster Tycoon 2
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 baru dengan dokumentasi. Anda juga dapat menavigasi kode dengan mengklik nomor baris dengan peringatan penganalisis. Navigasi dari komputer jarak jauh dimungkinkan saat menggunakan SumberTreeRoot penanda. Siapa pun yang tertarik dengan mode pengoperasian penganalisis ini dapat membiasakan diri dengan bagian terkait dokumentasi.

Melihat hasil analisa

Sekarang setelah kita selesai menerapkan dan mengonfigurasi build, mari kita lihat beberapa peringatan menarik yang ditemukan dalam proyek yang sedang kita lihat.

Peringatan N1

V773 [CWE-401] Pengecualian dilempar 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 setelah mengalokasikan memori secara dinamis BuatObjek, ketika pengecualian terjadi, memori tidak dihapus, dan terjadi kebocoran memori.

Peringatan N2

V501 Ada sub-ekspresi yang 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 selain penganalisis statis yang dapat lulus tes perhatian ini. Contoh salin-tempel ini bagus karena alasan ini.

Peringatan N3

V703 Anehnya, bidang 'bendera' di kelas turunan 'RCT12BannerElement' menimpa bidang di kelas dasar 'RCT12TileElementBase'. Periksa baris: 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 turunan tidak selalu merupakan kesalahan. Namun, teknologi pewarisan sendiri mengasumsikan bahwa semua bidang kelas induk ada di kelas anak. Dengan mendeklarasikan bidang dengan nama yang sama pada ahli waris, kita menciptakan kebingungan.

Peringatan N4

V793 Anehnya, hasil pernyataan 'imageDirection / 8' merupakan bagian dari kondisi. Mungkin pernyataan ini seharusnya dibandingkan dengan pernyataan lain. libopenrct2 ObservationTower.cpp 38

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

Mari kita lihat lebih dekat. Ekspresi arah gambar/8 akan salah jika arah gambar berada pada kisaran -7 hingga 7. Bagian kedua: (arah gambar / 8) != 3 pemeriksaan arah gambar karena berada di luar rentang: masing-masing dari -31 hingga -24 dan dari 24 hingga 31. Tampaknya cukup aneh bagi saya untuk memeriksa nomor untuk dimasukkan dalam rentang tertentu dengan cara ini dan, meskipun tidak ada kesalahan dalam potongan kode ini, saya akan merekomendasikan untuk menulis ulang ketentuan ini agar lebih eksplisit. Ini akan membuat hidup lebih mudah bagi orang-orang yang membaca dan memelihara kode ini.

Peringatan N5

V587 Urutan tugas ganjil semacam ini: A = B; B = SEBUAH;. 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;
      ....
  }
  ....
}

Fragmen kode ini kemungkinan besar diperoleh melalui dekompilasi. Kemudian, dilihat dari komentar yang tersisa, sebagian dari kode yang tidak berfungsi telah dihapus. Namun, masih ada beberapa operasi lagi kursorId, yang juga tidak masuk akal.

Peringatan N6

V1004 [CWE-476] Penunjuk 'pemain' digunakan secara tidak aman setelah diverifikasi terhadap nullptr. Periksa baris: 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 mudah untuk diperbaiki, Anda hanya perlu memeriksanya untuk ketiga kalinya pemain ke penunjuk nol, atau menambahkannya ke badan pernyataan kondisional. Saya akan 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);
    }
  }
  ....
}

Peringatan N7

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 menghilangkan baris kode yang sulit dibaca dalam satu gerakan dan menyelesaikan masalah dengan memeriksa nullptr. Saya sarankan 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();
  }
}

Kodenya terlihat sangat aneh. Menurut saya ada kesalahan ketik baik pada kondisi atau saat menetapkan ulang variabel ColumnHeaderPressedCurrentState nilai-nilai palsu.

Keluaran

Seperti yang bisa kita lihat, mengintegrasikan penganalisis statis PVS-Studio ke dalam proyek TeamCity Anda cukup sederhana. Untuk melakukan ini, cukup menulis satu file konfigurasi kecil saja. Memeriksa kode akan memungkinkan Anda mengidentifikasi masalah segera setelah perakitan, yang akan membantu menghilangkannya ketika kompleksitas dan biaya perubahan masih rendah.

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

Sumber: www.habr.com

Tambah komentar