PVS-Studio va uzluksiz integratsiya: TeamCity. Open RollerCoaster Tycoon 2 loyihasining tahlili

PVS-Studio va uzluksiz integratsiya: TeamCity. Open RollerCoaster Tycoon 2 loyihasining tahlili
PVS-Studio analizatoridan foydalanishning eng dolzarb stsenariylaridan biri uning CI tizimlari bilan integratsiyalashuvidir. Va deyarli har qanday uzluksiz integratsiya tizimidan PVS-Studio loyihasini tahlil qilish faqat bir nechta buyruqlarga o'rnatilishi mumkin bo'lsa-da, biz bu jarayonni yanada qulayroq qilishda davom etamiz. PVS-Studio endi analizator chiqishini TeamCity - TeamCity Inspections Type formatiga aylantirishni qo'llab-quvvatlaydi. Keling, bu qanday ishlashini ko'rib chiqaylik.

Amaldagi dasturiy ta'minot haqida ma'lumot

PVS-studiyasi β€” C, C++, C# va Java kodlarining statik analizatori, har xil turdagi xatolarni topish va tuzatish vazifasini osonlashtirish uchun mo'ljallangan. Analizatordan Windows, Linux va macOS tizimlarida foydalanish mumkin. Ushbu maqolada biz nafaqat analizatorning o'zi, balki uning tarqatilishidan ba'zi yordamchi dasturlardan ham faol foydalanamiz.

CLMMonitor β€” kompilyator ishga tushirilishini kuzatuvchi monitoring serveri. Loyihangizni qurishni boshlashdan oldin uni darhol ishga tushirish kerak. Snooping rejimida server barcha qo'llab-quvvatlanadigan kompilyatorlarning ishlashini to'xtatadi. Shuni ta'kidlash kerakki, ushbu yordamchi dastur faqat C/C++ loyihalarini tahlil qilish uchun ishlatilishi mumkin.

PlogKonverter - analizator hisobotlarini turli formatlarga aylantirish uchun yordamchi dastur.

O'rganilayotgan loyiha haqida ma'lumot

Keling, ushbu funksiyani amaliy misolda sinab ko'raylik - OpenRCT2 loyihasini tahlil qilaylik.

OpenRCT2 - RollerCoaster Tycoon 2 (RCT2) o'yinining ochiq amalga oshirilishi, uni yangi funktsiyalar bilan kengaytirish va xatolarni tuzatish. O'yin attraksionlar, do'konlar va ob'ektlarni o'z ichiga olgan istirohat bog'ini qurish va saqlash atrofida aylanadi. O'yinchi daromad olishga harakat qilishi va mehmonlarni xursand qilish bilan birga parkning yaxshi obro'sini saqlab qolishga harakat qilishi kerak. OpenRCT2 sizga stsenariyda ham, sinov muhitida ham o'ynash imkonini beradi. Ssenariylar o'yinchidan ma'lum bir vazifani belgilangan vaqt ichida bajarishni talab qiladi, Sandbox esa o'yinchiga hech qanday cheklovlarsiz yoki moliyaviy mablag'larsiz yanada moslashuvchan parkni qurishga imkon beradi.

moslashish

Vaqtni tejash uchun men o'rnatish jarayonini o'tkazib yuboraman va kompyuterimda TeamCity serveri ishlayotgan paytdan boshlayman. Bizga o'tishimiz kerak: localhost:{o'rnatish jarayonida ko'rsatilgan port} (mening holimda, localhost:9090) va avtorizatsiya ma'lumotlarini kiriting. Kirishdan so'ng bizni kutib oladi:

PVS-Studio va uzluksiz integratsiya: TeamCity. Open RollerCoaster Tycoon 2 loyihasining tahlili
Loyiha yaratish tugmasini bosing. Keyin qo'lda-ni tanlang va maydonlarni to'ldiring.

PVS-Studio va uzluksiz integratsiya: TeamCity. Open RollerCoaster Tycoon 2 loyihasining tahlili
Tugmani bosgandan so'ng yaratish, bizni sozlamalar bilan oyna kutib oladi.

PVS-Studio va uzluksiz integratsiya: TeamCity. Open RollerCoaster Tycoon 2 loyihasining tahlili
Keling, bosing Qurilish konfiguratsiyasini yarating.

PVS-Studio va uzluksiz integratsiya: TeamCity. Open RollerCoaster Tycoon 2 loyihasining tahlili
Maydonlarni to'ldiring va ustiga bosing yaratish. Biz versiyani boshqarish tizimini tanlashni so'ragan oynani ko'ramiz. Manbalar allaqachon mahalliy sifatida joylashganligi sababli, bosing Siz identifikatsiyadan o'tmadingiz.

PVS-Studio va uzluksiz integratsiya: TeamCity. Open RollerCoaster Tycoon 2 loyihasining tahlili
Nihoyat, biz loyiha sozlamalariga o'tamiz.

PVS-Studio va uzluksiz integratsiya: TeamCity. Open RollerCoaster Tycoon 2 loyihasining tahlili
Keling, yig'ish bosqichlarini qo'shamiz, buni amalga oshirish uchun bosing: Qurilish bosqichlari -> Qurilish bosqichini qo'shish.

PVS-Studio va uzluksiz integratsiya: TeamCity. Open RollerCoaster Tycoon 2 loyihasining tahlili
Bu erda biz tanlaymiz:

  • Yuguruvchi turi -> Buyruqlar qatori
  • Ishga tushirish -> Maxsus skript

Loyihani tuzishda biz tahlilni amalga oshirganimiz sababli, yig'ish va tahlil qilish bir qadam bo'lishi kerak, shuning uchun maydonni to'ldiring Maxsus skript:

PVS-Studio va uzluksiz integratsiya: TeamCity. Open RollerCoaster Tycoon 2 loyihasining tahlili
Keyinchalik individual qadamlarni ko'rib chiqamiz. Analizatorni yuklash, loyihani yig'ish, uni tahlil qilish, hisobotni chiqarish va uni formatlash uchun faqat o'n bir qator kod kerak bo'lishi muhimdir.

Biz qilishimiz kerak bo'lgan oxirgi narsa - atrof-muhit o'zgaruvchilarini o'rnatish, men ularni o'qishni yaxshilashning ba'zi usullarini aytib berdim. Buning uchun keling, davom etaylik: Parametrlar -> Yangi parametr qo'shish va uchta o'zgaruvchini qo'shing:

PVS-Studio va uzluksiz integratsiya: TeamCity. Open RollerCoaster Tycoon 2 loyihasining tahlili
Buning uchun tugmani bosish kifoya yugurish yuqori o'ng burchakda. Loyiha yig'ilib, tahlil qilinayotganda, men sizga skript haqida gapirib beraman.

To'g'ridan-to'g'ri skript

Birinchidan, biz eng so'nggi PVS-Studio distributivini yuklab olishimiz kerak. Buning uchun biz Chocolatey paket menejeridan foydalanamiz. Bu haqda ko'proq bilmoqchi bo'lganlar uchun mos keladigan narsa mavjud maqola:

choco install pvs-studio -y

Keyinchalik, CLMonitor loyihasini kuzatish dasturini ishga tushiramiz.

%CLmon% monitor –-attach

Keyin biz loyihani muhit o'zgaruvchisi sifatida quramiz MSB men qurishim kerak bo'lgan MSBuild versiyasiga yo'l

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

PVS-Studio uchun login va litsenziya kalitini kiritamiz:

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

Qurilish tugagandan so'ng, qayta ishlangan fayllar va statik tahlillarni yaratish uchun CLMonitor-ni qayta ishga tushiring:

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

Keyin biz tarqatishimizdan boshqa yordamchi dasturdan foydalanamiz. PlogConverter hisobotni standart formatdan TeamCity-ga xos formatga o'zgartiradi. Buning yordamida biz uni to'g'ridan-to'g'ri qurilish oynasida ko'rishimiz mumkin bo'ladi.

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

Oxirgi qadam formatlangan hisobotni ko'rsatishdir stdout, u TeamCity tahlilchisi tomonidan olinadi.

type "C:tempptest.plog_TeamCity.txt"

To'liq skript kodi:

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"

Ayni paytda, loyihani yig'ish va tahlil qilish muvaffaqiyatli yakunlandi, biz tabga o'tishimiz mumkin loyihalar i ubeditsya v etom.

PVS-Studio va uzluksiz integratsiya: TeamCity. Open RollerCoaster Tycoon 2 loyihasining tahlili
Endi bosing Tekshirishlar jamianalizator hisobotini ko'rish uchun:

PVS-Studio va uzluksiz integratsiya: TeamCity. Open RollerCoaster Tycoon 2 loyihasining tahlili
Ogohlantirishlar diagnostika qoidalari raqamlari bo'yicha guruhlangan. Kod bo'ylab harakat qilish uchun siz ogohlantirish bilan chiziq raqamini bosishingiz kerak. Yuqori o'ng burchakdagi savol belgisini bosish sizga hujjatlar bilan yangi yorliqni ochadi. Shuningdek, analizator ogohlantirishi bilan chiziq raqamini bosish orqali kod bo'ylab harakat qilishingiz mumkin. Foydalanishda masofaviy kompyuterdan navigatsiya qilish mumkin SourceTreeRoot marker. Analizatorning ushbu ishlash rejimiga qiziqqan har bir kishi tegishli bo'lim bilan tanishishi mumkin hujjatlar.

Analizator natijalarini ko'rish

Qurilishni joylashtirish va sozlashni tugatganimizdan so'ng, biz ko'rib chiqayotgan loyihada topilgan ba'zi qiziqarli ogohlantirishlarni ko'rib chiqaylik.

Ogohlantirish N1

V773 [CWE-401] Istisno "natija" ko'rsatgichini chiqarmasdan tashlandi. Xotiraning oqishi mumkin. 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 xotirani dinamik ravishda taqsimlagandan so'ng xatolikni payqadi Ob'ekt yaratish, istisno sodir bo'lganda, xotira o'chirilmaydi va xotira oqishi sodir bo'ladi.

Ogohlantirish N2

V501 '|' belgisining chap va o'ng tomonida bir xil '(1ULL << WIDX_MONTH_BOX)' pastki ifodalar mavjud. 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 tashqari kam sonli odamlar ushbu diqqatlilik testidan o'tishlari mumkin edi. Ushbu nusxa ko'chirish va joylashtirish misoli aynan shu sababga ko'ra yaxshi.

Ogohlantirishlar N3

V703 "RCT12BannerElement" olingan sinfidagi "bayroqlar" maydoni "RCT12TileElementBase" asosiy sinfidagi maydonni qayta yozishi g'alati. Tekshirish chiziqlari: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570

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

Albatta, asosiy sinfda va avlodda bir xil nomdagi o'zgaruvchidan foydalanish har doim ham xato emas. Biroq, meros texnologiyasining o'zi ota-onalar sinfining barcha sohalari bolalar sinfida mavjud deb taxmin qiladi. Vorisda bir xil nomdagi maydonlarni e'lon qilish orqali biz chalkashliklarni keltirib chiqaramiz.

Ogohlantirish N4

V793 "ImageDirection / 8" iborasining natijasi shartning bir qismi ekanligi g'alati. Ehtimol, bu bayonotni boshqa narsa bilan solishtirish kerak edi. libopenrct2 ObservationTower.cpp 38

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

Keling, batafsil ko'rib chiqaylik. Ifoda imageDirect/8 bo'lsa yolg'on bo'ladi tasvir yo'nalishi -7 dan 7 gacha bo'lgan oraliqda. Ikkinchi qism: (imageDirection / 8) != 3 cheklar tasvir yo'nalishi diapazondan tashqarida bo'lganligi uchun: mos ravishda -31 dan -24 gacha va 24 dan 31 gacha. Raqamlarni ma'lum bir diapazonga kiritish uchun shu tarzda tekshirish men uchun juda g'alati tuyuladi va agar ushbu kod qismida xato bo'lmasa ham, men ushbu shartlarni aniqroq bo'lishi uchun qayta yozishni tavsiya qilaman. Bu ushbu kodni o'qiydigan va saqlaydigan odamlar uchun hayotni ancha osonlashtiradi.

Ogohlantirish N5

V587 Bunday turdagi topshiriqlarning g'alati ketma-ketligi: A = B; B = A;. Tekshirish qatorlari: 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;
      ....
  }
  ....
}

Ushbu kod bo'lagi katta ehtimol bilan dekompilyatsiya orqali olingan. Keyin, qolgan sharhga ko'ra, ishlamaydigan kodning bir qismi olib tashlandi. Biroq, hali bir nechta operatsiyalar bor kursor identifikatori, bu ham juda mantiqiy emas.

Ogohlantirish N6

V1004 [CWE-476] "O'yinchi" ko'rsatkichi nullptr bilan tekshirilgandan so'ng ishonchsiz ishlatilgan. Tekshirish qatorlari: 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);                    // <=
  }
  ....
}

Ushbu kodni tuzatish juda oson, siz uni uchinchi marta tekshirishingiz kerak o'yinchi null ko'rsatkichga yoki uni shartli bayonotning asosiy qismiga qo'shing. Men ikkinchi variantni taklif qilaman:

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

Ogohlantirish N7

V547 [CWE-570] "name == nullptr" iborasi har doim noto'g'ri. 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));
    ....
  }
  ....
}

Siz o'qish qiyin bo'lgan kod qatoridan bir zarbada xalos bo'lishingiz va muammoni tekshirish orqali hal qilishingiz mumkin. nullptr. Men kodni quyidagi tarzda o'zgartirishni taklif qilaman:

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

Ogohlantirish N8

V1048 [CWE-1164] "ColumnHeaderPressedCurrentState" o'zgaruvchisiga bir xil qiymat tayinlangan. libopenrct2ui CustomListView.cpp 510

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

Kod juda g'alati ko'rinadi. Menimcha, shartda yoki o'zgaruvchini qayta tayinlashda xatolik yuz berdi ColumnHeaderPressedCurrentState ma'no yolg'on.

xulosa

Ko'rib turganimizdek, PVS-Studio statik analizatorini TeamCity loyihangizga integratsiya qilish juda oddiy. Buning uchun faqat bitta kichik konfiguratsiya faylini yozish kifoya. Kodni tekshirish sizni montajdan so'ng darhol muammolarni aniqlash imkonini beradi, bu esa o'zgarishlarning murakkabligi va narxi hali ham past bo'lganda ularni bartaraf etishga yordam beradi.

PVS-Studio va uzluksiz integratsiya: TeamCity. Open RollerCoaster Tycoon 2 loyihasining tahlili
Agar siz ushbu maqolani ingliz tilida so'zlashuvchi auditoriya bilan baham ko'rmoqchi bo'lsangiz, tarjima havolasidan foydalaning: Vladislav Stolyarov. PVS-Studio va uzluksiz integratsiya: TeamCity. Open RollerCoaster Tycoon 2 loyihasining tahlili.

Manba: www.habr.com

a Izoh qo'shish