PVS-Studio dan Integrasi Berterusan: TeamCity. Analisis projek Open RollerCoaster Tycoon 2

PVS-Studio dan Integrasi Berterusan: TeamCity. Analisis projek Open RollerCoaster Tycoon 2
Salah satu senario terkini untuk menggunakan penganalisis PVS-Studio ialah penyepaduannya dengan sistem CI. Dan walaupun analisis projek PVS-Studio daripada hampir mana-mana sistem integrasi berterusan boleh dibina ke dalam beberapa arahan sahaja, kami terus menjadikan proses ini lebih mudah. PVS-Studio kini mempunyai sokongan untuk menukar output penganalisis kepada format untuk TeamCity - Jenis Pemeriksaan TeamCity. Mari lihat bagaimana ia berfungsi.

Maklumat tentang perisian yang digunakan

PVS-Studio β€” penganalisis statik bagi kod C, C++, C# dan Java, direka untuk memudahkan tugas mencari dan membetulkan pelbagai jenis ralat. Penganalisis boleh digunakan pada Windows, Linux dan macOS. Dalam artikel ini kami akan secara aktif menggunakan bukan sahaja penganalisis itu sendiri, tetapi juga beberapa utiliti daripada pengedarannya.

CLMonitor β€” ialah pelayan pemantauan yang memantau pelancaran pengkompil. Ia mesti dijalankan dengan segera sebelum mula membina projek anda. Dalam mod mengintip, pelayan akan memintas larian semua penyusun yang disokong. Perlu diingat bahawa utiliti ini hanya boleh digunakan untuk menganalisis projek C/C++.

PlogConverter – utiliti untuk menukar laporan penganalisis kepada format yang berbeza.

Maklumat tentang projek yang sedang dikaji

Mari cuba fungsi ini dengan contoh praktikal - mari analisa projek OpenRCT2.

BukaRCT2 - pelaksanaan terbuka permainan RollerCoaster Tycoon 2 (RCT2), mengembangkannya dengan fungsi baharu dan membetulkan pepijat. Permainan berkisar tentang membina dan menyelenggara taman hiburan yang mengandungi tunggangan, kedai dan kemudahan. Pemain mesti cuba membuat keuntungan dan mengekalkan reputasi baik taman sambil memastikan tetamu gembira. OpenRCT2 membolehkan anda bermain dalam kedua-dua senario dan kotak pasir. Senario memerlukan pemain menyelesaikan tugas tertentu dalam masa yang ditetapkan, manakala Sandbox membenarkan pemain membina taman yang lebih fleksibel tanpa sebarang sekatan atau kewangan.

pelarasan

Untuk menjimatkan masa, saya mungkin akan melangkau proses pemasangan dan bermula dari saat saya menjalankan pelayan TeamCity pada komputer saya. Kita perlu pergi ke: localhost:{port specified during the installation process} (dalam kes saya, localhost:9090) dan masukkan data kebenaran. Selepas masuk kami akan disambut oleh:

PVS-Studio dan Integrasi Berterusan: TeamCity. Analisis projek Open RollerCoaster Tycoon 2
Klik pada butang Cipta Projek. Seterusnya, pilih Secara Manual dan isikan medan.

PVS-Studio dan Integrasi Berterusan: TeamCity. Analisis projek Open RollerCoaster Tycoon 2
Selepas menekan butang Buat, kami disambut oleh tetingkap dengan tetapan.

PVS-Studio dan Integrasi Berterusan: TeamCity. Analisis projek Open RollerCoaster Tycoon 2
Jom klik Buat konfigurasi binaan.

PVS-Studio dan Integrasi Berterusan: TeamCity. Analisis projek Open RollerCoaster Tycoon 2
Isi medan dan klik Buat. Kami melihat tetingkap yang meminta anda memilih sistem kawalan versi. Memandangkan sumber telah pun berada di dalam negara, klik Langkau.

PVS-Studio dan Integrasi Berterusan: TeamCity. Analisis projek Open RollerCoaster Tycoon 2
Akhirnya, kita beralih ke tetapan projek.

PVS-Studio dan Integrasi Berterusan: TeamCity. Analisis projek Open RollerCoaster Tycoon 2
Mari tambah langkah pemasangan, untuk melakukan klik ini: Langkah bina -> Tambah langkah binaan.

PVS-Studio dan Integrasi Berterusan: TeamCity. Analisis projek Open RollerCoaster Tycoon 2
Di sini kami memilih:

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

Memandangkan kami akan melakukan analisis semasa penyusunan projek, pemasangan dan analisis harus menjadi satu langkah, jadi isikan medan tersebut Skrip Tersuai:

PVS-Studio dan Integrasi Berterusan: TeamCity. Analisis projek Open RollerCoaster Tycoon 2
Kami akan melihat langkah individu kemudian. Adalah penting untuk memuatkan penganalisis, memasang projek, menganalisisnya, mengeluarkan laporan dan memformatnya hanya memerlukan sebelas baris kod.

Perkara terakhir yang perlu kita lakukan ialah menetapkan pembolehubah persekitaran, yang telah saya gariskan beberapa cara untuk meningkatkan kebolehbacaannya. Untuk melakukan ini, mari kita teruskan: Parameter -> Tambah parameter baharu dan tambah tiga pembolehubah:

PVS-Studio dan Integrasi Berterusan: TeamCity. Analisis projek Open RollerCoaster Tycoon 2
Anda hanya perlu menekan butang Main di sudut kanan atas. Semasa projek sedang dipasang dan dianalisis, saya akan memberitahu anda tentang skrip.

Skrip langsung

Pertama, kita perlu memuat turun pengedaran PVS-Studio terkini. Untuk ini kami menggunakan pengurus pakej Chocolatey. Bagi mereka yang ingin mengetahui lebih lanjut mengenai perkara ini, ada yang sepadan artikel:

choco install pvs-studio -y

Seterusnya, mari lancarkan utiliti penjejakan binaan projek CLMonitor.

%CLmon% monitor –-attach

Kemudian kami akan membina projek sebagai pembolehubah persekitaran MSB ialah laluan ke versi MSBuild yang perlu saya bina

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

Mari masukkan kunci log masuk dan lesen untuk PVS-Studio:

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

Selepas binaan selesai, jalankan CLMonitor sekali lagi untuk menjana fail praproses dan analisis statik:

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

Kemudian kami akan menggunakan utiliti lain daripada pengedaran kami. PlogConverter menukar laporan daripada format standard kepada format khusus TeamCity. Terima kasih kepada ini, kami akan dapat melihatnya terus dalam tetingkap binaan.

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

Langkah terakhir ialah memaparkan laporan berformat dalam stdout, di mana ia akan diambil oleh penghurai TeamCity.

type "C:tempptest.plog_TeamCity.txt"

Kod skrip penuh:

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, pemasangan dan analisis projek telah berjaya diselesaikan, kita boleh pergi ke tab Projek dan pastikan ia.

PVS-Studio dan Integrasi Berterusan: TeamCity. Analisis projek Open RollerCoaster Tycoon 2
Sekarang mari kita klik pada Jumlah Pemeriksaanuntuk pergi melihat laporan penganalisis:

PVS-Studio dan Integrasi Berterusan: TeamCity. Analisis projek Open RollerCoaster Tycoon 2
Amaran dikumpulkan mengikut nombor peraturan diagnostik. Untuk menavigasi kod, anda perlu mengklik pada nombor baris dengan amaran. Mengklik pada tanda soal di penjuru kanan sebelah atas akan membuka anda tab baharu dengan dokumentasi. Anda juga boleh menavigasi kod dengan mengklik pada nombor baris dengan amaran penganalisis. Navigasi dari komputer jauh adalah mungkin apabila menggunakan SourceTreeRoot penanda. Sesiapa yang berminat dengan mod operasi penganalisis ini boleh membiasakan diri dengan bahagian yang sepadan dokumentasi.

Melihat keputusan penganalisis

Sekarang setelah kita selesai mengatur dan mengkonfigurasi binaan, mari kita lihat beberapa amaran menarik yang terdapat dalam projek yang sedang kita lihat.

Amaran N1

V773 [CWE-401] Pengecualian dilemparkan tanpa melepaskan penunjuk 'hasil'. Kebocoran ingatan mungkin berlaku. 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 ralat yang selepas memperuntukkan memori secara dinamik CreateObject, apabila pengecualian berlaku, memori tidak dikosongkan, dan kebocoran memori berlaku.

Amaran N2

V501 Terdapat sub-ungkapan yang sama '(1ULL << WIDX_MONTH_BOX)' di sebelah kiri dan di sebelah kanan '|' pengendali. 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),
  ....
};

Beberapa orang selain daripada penganalisis statik boleh lulus ujian perhatian ini. Contoh salin-tampal ini bagus untuk sebab ini.

Amaran N3

V703 Adalah ganjil bahawa medan 'bendera' dalam kelas terbitan 'RCT12BannerElement' menimpa medan dalam kelas asas 'RCT12TileElementBase'. Semak baris: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570

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

Sudah tentu, menggunakan pembolehubah dengan nama yang sama dalam kelas asas dan dalam keturunan tidak selalu menjadi ralat. Walau bagaimanapun, teknologi pewarisan sendiri menganggap bahawa semua bidang kelas induk terdapat dalam kelas anak. Dengan mengisytiharkan medan dengan nama yang sama dalam waris, kami memperkenalkan kekeliruan.

Amaran N4

V793 Adalah ganjil bahawa hasil pernyataan 'imageDirection / 8' adalah sebahagian daripada syarat. Mungkin, kenyataan ini sepatutnya dibandingkan dengan sesuatu yang lain. libopenrct2 ObservationTower.cpp 38

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

Mari kita lihat lebih dekat. Ungkapan gambarArah/8 akan menjadi palsu jika gambarArah berada dalam julat dari -7 hingga 7. Bahagian kedua: (gambarArah / 8) != 3 cek gambarArah kerana berada di luar julat: dari -31 hingga -24 dan dari 24 hingga 31, masing-masing. Nampaknya agak pelik bagi saya untuk menyemak nombor untuk dimasukkan dalam julat tertentu dengan cara ini dan, walaupun tiada ralat dalam sekeping kod ini, saya akan mengesyorkan agar anda menulis semula syarat ini untuk menjadi lebih jelas. Ini akan menjadikan hidup lebih mudah bagi orang yang akan membaca dan mengekalkan kod ini.

Amaran N5

V587 Urutan tugasan ganjil seperti ini: A = B; B = A;. Semak talian: 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;
      ....
  }
  ....
}

Serpihan kod ini berkemungkinan besar diperoleh melalui penyahkompilasi. Kemudian, berdasarkan ulasan yang ditinggalkan, sebahagian daripada kod yang tidak berfungsi telah dialih keluar. Walau bagaimanapun, masih terdapat beberapa operasi lagi cursorId, yang juga tidak masuk akal.

Amaran N6

V1004 [CWE-476] Penunjuk 'pemain' digunakan secara tidak selamat selepas ia disahkan terhadap nullptr. Semak talian: 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);                    // <=
  }
  ....
}

Kod ini agak mudah untuk dibetulkan; anda hanya perlu menyemaknya untuk kali ketiga Pemain pada penuding nol, atau tambahkannya pada isi pernyataan bersyarat. Saya akan mencadangkan pilihan 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);
    }
  }
  ....
}

Amaran N7

V547 [CWE-570] Ungkapan 'nama == nullptr' sentiasa palsu. 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 boleh menyingkirkan baris kod yang sukar dibaca dalam satu masa dan menyelesaikan masalah dengan menyemak nullptr. Saya cadangkan menukar kod seperti 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);
    ....
  }
  ....
}

Amaran N8

V1048 [CWE-1164] Pembolehubah 'ColumnHeaderPressedCurrentState' telah diberikan nilai yang sama. libopenrct2ui CustomListView.cpp 510

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

Kod itu kelihatan agak pelik. Nampaknya saya ada kesilapan menaip sama ada dalam keadaan atau semasa menetapkan semula pembolehubah ColumnHeaderPressedCurrentState makna palsu.

Output

Seperti yang kita dapat lihat, menyepadukan penganalisis statik PVS-Studio ke dalam projek TeamCity anda agak mudah. Untuk melakukan ini, cukup untuk menulis hanya satu fail konfigurasi kecil. Menyemak kod akan membolehkan anda mengenal pasti masalah sejurus selepas pemasangan, yang akan membantu menghapuskannya apabila kerumitan dan kos perubahan masih rendah.

PVS-Studio dan Integrasi Berterusan: TeamCity. Analisis projek Open RollerCoaster Tycoon 2
Jika anda ingin berkongsi artikel ini dengan penonton berbahasa Inggeris, sila gunakan pautan terjemahan: Vladislav Stolyarov. PVS-Studio dan Integrasi Berterusan: TeamCity. Analisis projek Open RollerCoaster Tycoon 2.

Sumber: www.habr.com

Tambah komen