PVS-Studio و ادغام مداوم: TeamCity. تحلیل پروژه Open RollerCoaster Tycoon 2

PVS-Studio و ادغام مداوم: TeamCity. تحلیل پروژه Open RollerCoaster Tycoon 2
یکی از جدیدترین سناریوها برای استفاده از آنالایزر PVS-Studio، ادغام آن با سیستم های CI است. و اگرچه تجزیه و تحلیل یک پروژه PVS-Studio از تقریباً هر سیستم یکپارچه سازی پیوسته را می توان تنها در چند دستور ایجاد کرد، ما همچنان این فرآیند را راحت تر می کنیم. PVS-Studio اکنون برای تبدیل خروجی تحلیلگر به قالبی برای TeamCity - TeamCity Inspections Type پشتیبانی می کند. بیایید ببینیم چگونه کار می کند.

اطلاعاتی در مورد نرم افزار مورد استفاده

PVS Studio - یک تحلیلگر استاتیک C، C++، C# و کد جاوا که برای تسهیل کار یافتن و تصحیح انواع مختلف خطاها طراحی شده است. آنالایزر را می توان در ویندوز، لینوکس و macOS استفاده کرد. در این مقاله ما نه تنها از خود آنالیزور بلکه از برخی ابزارهای توزیع آن نیز به طور فعال استفاده خواهیم کرد.

CLMonitor — یک سرور مانیتورینگ است که راه اندازی کامپایلر را نظارت می کند. باید بلافاصله قبل از شروع ساخت پروژه اجرا شود. در حالت snooping، سرور اجرای تمام کامپایلرهای پشتیبانی شده را متوقف می کند. شایان ذکر است که این ابزار فقط برای تجزیه و تحلیل پروژه های C/C++ قابل استفاده است.

PlogConverter – ابزاری برای تبدیل گزارش های تحلیلگر به فرمت های مختلف.

اطلاعات پروژه در دست مطالعه

بیایید این قابلیت را در یک مثال عملی امتحان کنیم - بیایید پروژه OpenRCT2 را تجزیه و تحلیل کنیم.

OpenRCT2 - اجرای باز بازی RollerCoaster Tycoon 2 (RCT2) که آن را با عملکردهای جدید گسترش داده و اشکالات را برطرف می کند. گیم پلی حول ساخت و نگهداری یک پارک تفریحی شامل وسایل سواری، مغازه ها و امکانات می چرخد. بازیکن باید سعی کند سود کند و شهرت خوب پارک را حفظ کند و در عین حال مهمانان را راضی نگه دارد. OpenRCT2 به شما امکان می دهد هم در سناریو و هم در سندباکس بازی کنید. سناریوها از بازیکن می خواهند که یک کار خاص را در یک زمان مشخص انجام دهد، در حالی که Sandbox به بازیکن اجازه می دهد تا پارکی انعطاف پذیرتر را بدون هیچ محدودیت یا مالی بسازد.

تنظیم

به منظور صرفه جویی در زمان، احتمالاً مراحل نصب را نادیده می گیرم و از لحظه ای که سرور TeamCity را روی رایانه خود اجرا می کنم، شروع می کنم. باید به آدرس: localhost:{port specified in the install process} (در مورد من localhost:9090) برویم و داده های مجوز را وارد کنیم. پس از ورود از ما استقبال می شود:

PVS-Studio و ادغام مداوم: TeamCity. تحلیل پروژه Open RollerCoaster Tycoon 2
روی دکمه Create Project کلیک کنید. بعد دستی را انتخاب کنید و فیلدها را پر کنید.

PVS-Studio و ادغام مداوم: TeamCity. تحلیل پروژه Open RollerCoaster Tycoon 2
پس از فشار دادن دکمه ساختن، با پنجره ای با تنظیمات روبرو می شویم.

PVS-Studio و ادغام مداوم: TeamCity. تحلیل پروژه Open RollerCoaster Tycoon 2
بیایید کلیک کنیم پیکربندی ساخت را ایجاد کنید.

PVS-Studio و ادغام مداوم: TeamCity. تحلیل پروژه Open RollerCoaster Tycoon 2
فیلدها را پر کرده و کلیک کنید ساختن. پنجره ای را می بینیم که از شما می خواهد یک سیستم کنترل نسخه را انتخاب کنید. از آنجایی که منابع از قبل به صورت محلی قرار دارند، کلیک کنید پرش.

PVS-Studio و ادغام مداوم: TeamCity. تحلیل پروژه Open RollerCoaster Tycoon 2
در نهایت به تنظیمات پروژه می رویم.

PVS-Studio و ادغام مداوم: TeamCity. تحلیل پروژه Open RollerCoaster Tycoon 2
بیایید مراحل اسمبلی را اضافه کنیم، برای انجام این کار کلیک کنید: مراحل ساخت -> مرحله ساخت را اضافه کنید.

PVS-Studio و ادغام مداوم: TeamCity. تحلیل پروژه Open RollerCoaster Tycoon 2
در اینجا ما انتخاب می کنیم:

  • نوع Runner -> Command Line
  • اجرا -> اسکریپت سفارشی

از آنجایی که در حین تدوین پروژه تحلیل انجام خواهیم داد، مونتاژ و آنالیز باید یک مرحله باشد، پس فیلد را پر کنید اسکریپت سفارشی:

PVS-Studio و ادغام مداوم: TeamCity. تحلیل پروژه Open RollerCoaster Tycoon 2
بعداً مراحل جداگانه را بررسی خواهیم کرد. مهم است که بارگذاری تحلیلگر، مونتاژ پروژه، تجزیه و تحلیل آن، خروجی گزارش و قالب بندی آن تنها به یازده خط کد نیاز دارد.

آخرین کاری که باید انجام دهیم این است که متغیرهای محیطی را تنظیم کنیم، که من روش هایی را برای بهبود خوانایی آنها بیان کرده ام. برای انجام این کار، به ادامه مطلب می پردازیم: پارامترها -> افزودن پارامتر جدید و سه متغیر اضافه کنید:

PVS-Studio و ادغام مداوم: TeamCity. تحلیل پروژه Open RollerCoaster Tycoon 2
تنها کاری که باید انجام دهید این است که دکمه را فشار دهید دویدن در گوشه سمت راست بالا در حالی که پروژه در حال مونتاژ و تجزیه و تحلیل است، من در مورد فیلمنامه به شما خواهم گفت.

اسکریپت مستقیم

ابتدا باید آخرین توزیع PVS-Studio را دانلود کنیم. برای این کار از Chocolatey package manager استفاده می کنیم. برای کسانی که می خواهند در این مورد بیشتر بدانند، یک متناظر وجود دارد مقاله:

choco install pvs-studio -y

در مرحله بعد، بیایید ابزار ردیابی ساخت پروژه CLMonitor را راه اندازی کنیم.

%CLmon% monitor –-attach

سپس پروژه را به عنوان یک متغیر محیطی می سازیم موسسات خدمات مالی مسیر نسخه MSBuild است که باید بسازم

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

بیایید کلید ورود و مجوز PVS-Studio را وارد کنیم:

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

پس از تکمیل ساخت، دوباره CLMonitor را اجرا کنید تا فایل های از پیش پردازش شده و تجزیه و تحلیل استاتیک تولید شود:

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

سپس از ابزار دیگری از توزیع خود استفاده خواهیم کرد. PlogConverter یک گزارش را از یک فرمت استاندارد به یک قالب خاص TeamCity تبدیل می کند. به لطف این، ما قادر خواهیم بود آن را مستقیماً در پنجره ساخت مشاهده کنیم.

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

آخرین مرحله نمایش گزارش فرمت شده است stdout، جایی که توسط تجزیه کننده TeamCity انتخاب می شود.

type "C:tempptest.plog_TeamCity.txt"

کد اسکریپت کامل:

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"

در ضمن مونتاژ و تحلیل پروژه با موفقیت به پایان رسیده است، می توانیم به تب پروژه ها و از آن مطمئن شوید

PVS-Studio و ادغام مداوم: TeamCity. تحلیل پروژه Open RollerCoaster Tycoon 2
حالا بیایید روی آن کلیک کنیم بازرسی کلبرای رفتن به مشاهده گزارش تحلیلگر:

PVS-Studio و ادغام مداوم: TeamCity. تحلیل پروژه Open RollerCoaster Tycoon 2
هشدارها بر اساس شماره قوانین تشخیصی گروه بندی می شوند. برای پیمایش از طریق کد، باید روی شماره خط با اخطار کلیک کنید. با کلیک بر روی علامت سوال در گوشه سمت راست بالا، یک برگه جدید با اسناد باز می شود. همچنین می توانید با کلیک بر روی شماره خط با هشدار آنالیزور در کد حرکت کنید. هنگام استفاده، ناوبری از رایانه راه دور امکان پذیر است SourceTreeRoot نشانگر هرکسی که به این حالت کار آنالایزر علاقه مند است می تواند با بخش مربوطه آشنا شود مستندات.

مشاهده نتایج تحلیلگر

اکنون که نصب و پیکربندی بیلد را به پایان رساندیم، بیایید نگاهی به هشدارهای جالبی که در پروژه ای که در حال بررسی آن هستیم، بیاندازیم.

هشدار N1

V773 [CWE-401] استثنا بدون رها کردن نشانگر «نتیجه» پرتاب شد. امکان نشت حافظه وجود دارد. 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;
}

تحلیلگر متوجه خطایی شد که پس از تخصیص دینامیک حافظه در CreateObject، هنگامی که یک استثنا رخ می دهد، حافظه پاک نمی شود و نشت حافظه رخ می دهد.

هشدار N2

V501 زیر عبارات یکسان "(1ULL << WIDX_MONTH_BOX)" در سمت چپ و سمت راست "|" وجود دارد. اپراتور. 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),
  ....
};

تعداد کمی از افراد غیر از یک آنالایزر استاتیک می توانند این تست توجه را پشت سر بگذارند. این مثال کپی پیست دقیقا به همین دلیل خوب است.

هشدارها N3

V703 عجیب است که فیلد "پرچم" در کلاس مشتق شده "RCT12BannerElement" فیلد در کلاس پایه "RCT12TileElementBase" را بازنویسی کند. بررسی خطوط: RCT12.h:570، RCT12.h:259. libopenrct2 RCT12.h 570

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

البته استفاده از متغیری با همین نام در کلاس پایه و در decendant همیشه خطا نیست. با این حال، خود فناوری وراثت فرض می‌کند که تمام فیلدهای کلاس والد در کلاس فرزند وجود دارد. با اعلام فیلدهایی با همین نام در وارث، سردرگمی ایجاد می کنیم.

هشدار N4

V793 عجیب است که نتیجه عبارت "imageDirection / 8" بخشی از شرط باشد. شاید باید این جمله را با چیز دیگری مقایسه می کرد. libopenrct2 ObservationTower.cpp 38

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

بیایید نگاه دقیق تری بیندازیم. اصطلاح imageDirection/8 دروغ خواهد بود اگر جهت تصویر در محدوده 7- تا 7 قرار دارد. بخش دوم: (imageDirection / 8) != 3 چک ها جهت تصویر برای خارج از محدوده: به ترتیب از 31- تا 24- و از 24 تا 31. برای من کاملاً عجیب است که اعداد را برای درج در محدوده خاصی به این روش بررسی کنم و حتی اگر در این قطعه کد خطایی وجود نداشته باشد، توصیه می کنم این شرایط را بازنویسی کنید تا واضح تر باشد. این امر زندگی را برای افرادی که این کد را می خوانند و حفظ می کنند بسیار آسان تر می کند.

هشدار N5

V587 دنباله ای عجیب از این نوع تکالیف: A = B; B = A;. بررسی خطوط: 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;
      ....
  }
  ....
}

این قطعه کد به احتمال زیاد با دیکامپایل به دست آمده است. سپس با قضاوت بر اساس کامنت گذاشته شده، بخشی از کد ناکارآمد حذف شد. با این حال، هنوز چند عملیات باقی مانده است نشانگر مکان نما، که همچنین چندان منطقی نیست.

هشدار N6

V1004 [CWE-476] نشانگر 'بازیکن' پس از تأیید در برابر nullptr به طور ناامن استفاده شد. بررسی خطوط: 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);                    // <=
  }
  ....
}

تصحیح این کد بسیار آسان است، فقط باید برای بار سوم آن را بررسی کنید بازیکن به یک اشاره گر تهی، یا آن را به متن دستور شرطی اضافه کنید. من گزینه دوم را پیشنهاد می کنم:

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

V547 [CWE-570] عبارت 'name == nullptr' همیشه نادرست است. 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));
    ....
  }
  ....
}

می توانید یک خط کد که به سختی خوانده می شود را در یک لحظه خلاص کنید و با بررسی مشکل را حل کنید. nullptr. پیشنهاد می کنم کد را به صورت زیر تغییر دهید:

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

هشدار N8

V1048 [CWE-1164] به متغیر "ColumnHeaderPressedCurrentState" همان مقدار اختصاص داده شد. libopenrct2ui CustomListView.cpp 510

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

کد بسیار عجیب به نظر می رسد. به نظر من اشتباه تایپی یا در شرط یا هنگام تخصیص مجدد متغیر وجود داشته است ColumnHeaderPressedCurrentState معانی غلط.

نتیجه

همانطور که می بینیم، ادغام آنالایزر استاتیک PVS-Studio در پروژه TeamCity بسیار ساده است. برای این کار کافی است فقط یک فایل پیکربندی کوچک بنویسید. بررسی کد به شما این امکان را می دهد که مشکلات را بلافاصله پس از مونتاژ شناسایی کنید، که به رفع آنها در زمانی که پیچیدگی و هزینه تغییرات هنوز کم است کمک می کند.

PVS-Studio و ادغام مداوم: TeamCity. تحلیل پروژه Open RollerCoaster Tycoon 2
اگر می خواهید این مقاله را با مخاطبان انگلیسی زبان به اشتراک بگذارید، لطفاً از پیوند ترجمه استفاده کنید: Vladislav Stolyarov. PVS-Studio و ادغام مداوم: TeamCity. تحلیل پروژه Open RollerCoaster Tycoon 2.

منبع: www.habr.com

اضافه کردن نظر