PVS-Studio və Davamlı İnteqrasiya: TeamCity. Open RollerCoaster Tycoon 2 layihəsinin təhlili

PVS-Studio və Davamlı İnteqrasiya: TeamCity. Open RollerCoaster Tycoon 2 layihəsinin təhlili
PVS-Studio analizatorundan istifadə üçün ən aktual ssenarilərdən biri onun CI sistemləri ilə inteqrasiyasıdır. Demək olar ki, hər hansı bir davamlı inteqrasiya sistemindən PVS-Studio layihəsinin təhlili yalnız bir neçə əmrə daxil edilə bilsə də, biz bu prosesi daha da rahatlaşdırmağa davam edirik. PVS-Studio indi analizatorun çıxışını TeamCity - TeamCity Təftiş Tipi formatına çevirmək dəstəyinə malikdir. Gəlin görək necə işləyir.

İstifadə olunan proqram təminatı haqqında məlumat

PVS Studio — C, C++, C# və Java kodlarının statik analizatoru, müxtəlif növ xətaları tapmaq və düzəltmək tapşırığını asanlaşdırmaq üçün nəzərdə tutulmuşdur. Analizator Windows, Linux və macOS sistemlərində istifadə edilə bilər. Bu yazıda biz yalnız analizatorun özündən deyil, həm də onun paylanmasından bəzi kommunallardan fəal şəkildə istifadə edəcəyik.

CLMMonitor — kompilyatorun işə salınmasına nəzarət edən monitorinq serveridir. Layihənizi qurmağa başlamazdan əvvəl dərhal işə salınmalıdır. Snooping rejimində server bütün dəstəklənən kompilyatorların işlərinə müdaxilə edəcək. Qeyd etmək lazımdır ki, bu yardım proqramı yalnız C/C++ layihələrini təhlil etmək üçün istifadə edilə bilər.

PlogConverter – analizator hesabatlarını müxtəlif formatlara çevirmək üçün köməkçi proqram.

Tədqiq olunan layihə haqqında məlumat

Gəlin bu funksionallığı praktiki nümunədə sınayaq - OpenRCT2 layihəsini təhlil edək.

OpenRCT2 - RollerCoaster Tycoon 2 (RCT2) oyununun açıq tətbiqi, onu yeni funksiyalarla genişləndirmək və səhvləri düzəltmək. Oyun attraksionlar, mağazalar və obyektlərdən ibarət əyləncə parkının qurulması və saxlanması ətrafında fırlanır. Oyunçu qazanc əldə etməyə və qonaqları xoşbəxt edərkən parkın yaxşı reputasiyasını qorumağa çalışmalıdır. OpenRCT2 həm ssenaridə, həm də sandboxda oynamağa imkan verir. Ssenarilər oyunçudan müəyyən edilmiş vaxt ərzində müəyyən tapşırığı yerinə yetirməsini tələb edir, Sandbox isə oyunçuya heç bir məhdudiyyət və maliyyə olmadan daha çevik park qurmağa imkan verir.

nizamlama

Vaxta qənaət etmək üçün yəqin ki, quraşdırma prosesini atlayacağam və kompüterimdə TeamCity serveri işlədiyi andan başlayacağam. Bizə getməliyik: localhost:{quraşdırma prosesi zamanı göstərilən port} (mənim vəziyyətimdə, localhost:9090) və avtorizasiya məlumatlarını daxil etməliyik. Girdikdən sonra bizi qarşılayacaqlar:

PVS-Studio və Davamlı İnteqrasiya: TeamCity. Open RollerCoaster Tycoon 2 layihəsinin təhlili
Layihə Yarat düyməsini basın. Sonra, Əl ilə seçin və sahələri doldurun.

PVS-Studio və Davamlı İnteqrasiya: TeamCity. Open RollerCoaster Tycoon 2 layihəsinin təhlili
Düyməni basdıqdan sonra Yaratmaq, bizi parametrləri olan bir pəncərə qarşılayır.

PVS-Studio və Davamlı İnteqrasiya: TeamCity. Open RollerCoaster Tycoon 2 layihəsinin təhlili
Gəlin klikləyək Quraşdırma konfiqurasiyası yaradın.

PVS-Studio və Davamlı İnteqrasiya: TeamCity. Open RollerCoaster Tycoon 2 layihəsinin təhlili
Sahələri doldurun və üzərinə klikləyin Yaratmaq. Versiya idarəetmə sistemini seçməyinizi xahiş edən bir pəncərə görürük. Mənbələr artıq yerli yerləşdiyindən, klikləyin Keçmək.

PVS-Studio və Davamlı İnteqrasiya: TeamCity. Open RollerCoaster Tycoon 2 layihəsinin təhlili
Nəhayət, layihə parametrlərinə keçirik.

PVS-Studio və Davamlı İnteqrasiya: TeamCity. Open RollerCoaster Tycoon 2 layihəsinin təhlili
Bunu etmək üçün montaj addımlarını əlavə edək: Quraşdırma addımları -> Quraşdırma addımı əlavə edin.

PVS-Studio və Davamlı İnteqrasiya: TeamCity. Open RollerCoaster Tycoon 2 layihəsinin təhlili
Burada seçirik:

  • Qaçış növü -> Komanda xətti
  • Çalıştır -> Xüsusi Skript

Layihənin tərtibi zamanı təhlil aparacağımız üçün montaj və təhlil bir addım olmalıdır, ona görə də sahəni doldurun Xüsusi skript:

PVS-Studio və Davamlı İnteqrasiya: TeamCity. Open RollerCoaster Tycoon 2 layihəsinin təhlili
Fərdi addımlara daha sonra baxacağıq. Analizatorun yüklənməsi, layihənin yığılması, təhlili, hesabatın çıxarılması və formatlaşdırılması yalnız on bir sətir kod tələb etməsi vacibdir.

Etməli olduğumuz son şey, onların oxunaqlılığını yaxşılaşdırmaq üçün bəzi yolları qeyd etdiyim mühit dəyişənlərini təyin etməkdir. Bunu etmək üçün davam edək: Parametrlər -> Yeni parametr əlavə edin və üç dəyişən əlavə edin:

PVS-Studio və Davamlı İnteqrasiya: TeamCity. Open RollerCoaster Tycoon 2 layihəsinin təhlili
Sizə lazım olan tək şey düyməni sıxmaqdır qaçış yuxarı sağ küncdə. Layihə yığılıb təhlil edilərkən mən sizə ssenari haqqında məlumat verəcəyəm.

Birbaşa skript

Əvvəlcə ən son PVS-Studio paylanmasını endirməliyik. Bunun üçün biz Chocolatey paket menecerindən istifadə edirik. Bu barədə daha çox bilmək istəyənlər üçün müvafiq var məqalə:

choco install pvs-studio -y

Sonra, CLMonitor layihəsinin qurulmasını izləmə proqramını işə salaq.

%CLmon% monitor –-attach

Sonra layihəni mühit dəyişəni kimi quracağıq MSB qurmalı olduğum MSBuild versiyasına gedən yoldur

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

PVS-Studio üçün giriş və lisenziya açarını daxil edək:

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

Quraşdırma tamamlandıqdan sonra əvvəlcədən işlənmiş fayllar və statik analiz yaratmaq üçün CLMonitor-u yenidən işə salın:

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

Sonra paylamamızdan başqa bir yardım proqramı istifadə edəcəyik. PlogConverter hesabatı standart formatdan TeamCity-ə məxsus formata çevirir. Bunun sayəsində biz onu birbaşa quraşdırma pəncərəsində görə biləcəyik.

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

Son addım formatlanmış hesabatı göstərməkdir stdout, TeamCity təhlilçisi tərəfindən alınacağı yer.

type "C:tempptest.plog_TeamCity.txt"

Tam skript kodu:

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"

Bu vaxt layihənin montajı və təhlili uğurla tamamlandı, sekmeye keçə bilərik Layihələr və buna əmin olun.

PVS-Studio və Davamlı İnteqrasiya: TeamCity. Open RollerCoaster Tycoon 2 layihəsinin təhlili
İndi klikləyək Yoxlamalar Cəmianalizator hesabatına baxmaq üçün:

PVS-Studio və Davamlı İnteqrasiya: TeamCity. Open RollerCoaster Tycoon 2 layihəsinin təhlili
Xəbərdarlıqlar diaqnostik qayda nömrələrinə görə qruplaşdırılır. Kodda naviqasiya etmək üçün xəbərdarlıq ilə sətir nömrəsinə klik etməlisiniz. Yuxarı sağ küncdəki sual işarəsinə klikləsəniz, sənədlərlə yeni bir tab açılacaqdır. Siz həmçinin analizator xəbərdarlığı ilə sətir nömrəsinə klikləməklə kod arasında gedə bilərsiniz. İstifadə edərkən uzaq kompüterdən naviqasiya mümkündür SourceTreeRoot marker. Analizatorun bu iş rejimi ilə maraqlanan hər kəs müvafiq bölmə ilə tanış ola bilər sənədləşdirmə.

Analizatorun nəticələrinə baxmaq

Quraşdırmanı yerləşdirməyi və konfiqurasiyanı bitirdiyimizə görə, gəlin baxdığımız layihədə tapılan bəzi maraqlı xəbərdarlıqlara nəzər salaq.

Xəbərdarlıq N1

V773 [CWE-401] İstisna "nəticə" göstəricisini buraxmadan atıldı. Yaddaş sızması mümkündür. 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;
}

Analizator yaddaşı dinamik şəkildə yerləşdirdikdən sonra bir səhv gördü Obyekt yaradın, istisna baş verdikdə, yaddaş təmizlənmir və yaddaş sızması baş verir.

Xəbərdarlıq N2

V501 '|'-nin solunda və sağında eyni alt ifadələr var '(1ULL << WIDX_MONTH_BOX)' 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),
  ....
};

Statik analizatordan başqa çox az adam bu diqqətlilik testindən keçə bilər. Bu kopyala-yapışdır nümunəsi məhz bu səbəbdən yaxşıdır.

Xəbərdarlıqlar N3

V703 Qəribədir ki, 'RCT12BannerElement' törəmə sinifindəki 'bayraqlar' sahəsi 'RCT12TileElementBase' baza sinifindəki sahənin üzərinə yazır. Yoxlama xətləri: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570

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

Təbii ki, eyni adlı dəyişənin əsas sinifdə və nəsildə istifadə edilməsi həmişə səhv deyil. Bununla belə, miras texnologiyasının özü ana sinifin bütün sahələrinin uşaq sinifdə mövcud olduğunu güman edir. Eyni adlı sahələri varisdə elan etməklə, biz qarışıqlıq yaradırıq.

Xəbərdarlıq N4

V793 Qəribədir ki, 'imageDirection / 8' ifadəsinin nəticəsi şərtin bir hissəsidir. Bəlkə də bu bəyanatı başqa bir şeylə müqayisə etmək lazım idi. libopenrct2 ObservationTower.cpp 38

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

Gəlin daha yaxından nəzər salaq. İfadə imageDirection/8 olsa yalan olacaq şəkil istiqaməti -7 ilə 7 aralığındadır. İkinci hissə: (imageDirection / 8) != 3 çeklər şəkil istiqaməti diapazondan kənarda olmaq üçün: müvafiq olaraq -31-dən -24 və 24-dən 31-ə qədər. Nömrələri müəyyən diapazona daxil etmək üçün bu şəkildə yoxlamaq mənə olduqca qəribə görünür və bu kod parçasında heç bir səhv olmasa belə, daha aydın olması üçün bu şərtləri yenidən yazmağı tövsiyə edərdim. Bu, bu kodu oxuyan və saxlayan insanların həyatını çox asanlaşdırar.

Xəbərdarlıq N5

V587 Bu cür tapşırıqların tək ardıcıllığı: A = B; B = A;. Yoxlama xətləri: 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;
      ....
  }
  ....
}

Bu kod parçası çox güman ki, dekompilyasiya yolu ilə əldə edilmişdir. Sonra, qalan şərhə əsasən, işləməyən kodun bir hissəsi silindi. Ancaq hələ bir neçə əməliyyat var kursorİd, bu da çox məna kəsb etmir.

Xəbərdarlıq N6

V1004 [CWE-476] "Oyunçu" göstəricisi nullptr ilə təsdiqləndikdən sonra etibarsız şəkildə istifadə edildi. Yoxlama xətləri: 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);                    // <=
  }
  ....
}

Bu kodu düzəltmək olduqca asandır, sadəcə onu üçüncü dəfə yoxlamaq lazımdır oyunçu null göstəriciyə və ya onu şərti ifadənin gövdəsinə əlavə edin. Mən ikinci variantı təklif edərdim:

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);
    }
  }
  ....
}

Xəbərdarlıq N7

V547 [CWE-570] 'ad == nullptr' ifadəsi həmişə yanlışdır. 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));
    ....
  }
  ....
}

Oxunması çətin olan kod sətirindən bir anda xilas ola və problemi yoxlayaraq həll edə bilərsiniz. nullptr. Kodu aşağıdakı kimi dəyişdirməyi təklif edirəm:

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);
    ....
  }
  ....
}

Xəbərdarlıq N8

V1048 [CWE-1164] 'ColumnHeaderPressedCurrentState' dəyişəninə eyni dəyər təyin edildi. libopenrct2ui CustomListView.cpp 510

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

Kod olduqca qəribə görünür. Mənə elə gəlir ki, ya şərtdə, ya da dəyişəni yenidən təyin edərkən hərf səhvi olub ColumnHeaderPressedCurrentState mənaları saxta.

Buraxılış

Gördüyümüz kimi, PVS-Studio statik analizatorunu TeamCity layihənizə inteqrasiya etmək olduqca sadədir. Bunun üçün yalnız bir kiçik konfiqurasiya faylı yazmaq kifayətdir. Kodun yoxlanılması, montajdan dərhal sonra problemləri müəyyən etməyə imkan verəcək, bu, dəyişikliklərin mürəkkəbliyi və dəyəri hələ də aşağı olduqda onları aradan qaldırmağa kömək edəcəkdir.

PVS-Studio və Davamlı İnteqrasiya: TeamCity. Open RollerCoaster Tycoon 2 layihəsinin təhlili
Bu məqaləni ingilisdilli auditoriya ilə bölüşmək istəyirsinizsə, tərcümə linkindən istifadə edin: Vladislav Stolyarov. PVS-Studio və Davamlı İnteqrasiya: TeamCity. Open RollerCoaster Tycoon 2 layihəsinin təhlili.

Mənbə: www.habr.com

Добавить комментарий