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-студиясы — C, C++, C# және Java кодтарының статикалық анализаторы, әр түрлі қателерді табу және түзету тапсырмасын жеңілдетуге арналған. Анализаторды Windows, Linux және macOS жүйелерінде пайдалануға болады. Бұл мақалада біз анализатордың өзін ғана емес, сонымен қатар оны таратудың кейбір утилиталарын белсенді түрде қолданамыз.

CLMonitor — компилятордың іске қосылуын бақылайтын мониторинг сервері. Ол жобаны құруды бастамас бұрын бірден іске қосылуы керек. Бақылау режимінде сервер барлық қолдау көрсетілетін компиляторлардың жұмысын тоқтатады. Айта кету керек, бұл утилитаны тек C/C++ жобаларын талдау үшін пайдалануға болады.

PlogConverter – анализатор есептерін әртүрлі пішімдерге түрлендіруге арналған қызметтік бағдарлама.

Зерттелетін жоба туралы ақпарат

Давайте попробуем данную функциональность на практическом примере – проанализируем проект OpenRCT2.

OpenRCT2 — открытая реализация игры RollerCoaster Tycoon 2 (RCT2), расширяющая её новыми функциями и исправляющая ошибки. Игровой процесс вращается вокруг строительства и содержания парка развлечений, в котором находятся аттракционы, магазины и объекты. Игрок должен постараться получить прибыль и поддерживать хорошую репутацию парка, сохраняя при этом гостей счастливыми. OpenRCT2 позволяет играть как в сценарии, так и в песочнице. Сценарии требуют, чтобы игрок выполнил определенную задачу в установленное время, в то время как песочница позволяет игроку построить более гибкий парк без каких-либо ограничений или финансов.

реттеу

Уақытты үнемдеу үшін мен орнату процесін өткізіп жіберіп, менің компьютерімде TeamCity сервері жұмыс істеп тұрған кезден бастайтын шығармын. Бізге келесіге өту керек: localhost:{орнату процесінде көрсетілген порт} (менің жағдайда, localhost:9090) және авторизация деректерін енгізіңіз. Кіргеннен кейін бізді қарсы алады:

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 жобасын талдау
Бұл әрекетті орындау үшін құрастыру қадамдарын қосамыз: Build steps -> Add build step.

PVS-Studio және үздіксіз интеграция: TeamCity. Open RollerCoaster Tycoon 2 жобасын талдау
Мұнда біз таңдаймыз:

  • Жүгірушінің түрі -> Пәрмен жолы
  • Іске қосу -> Теңшелетін сценарий

Біз жобаны құрастыру кезінде талдау жасайтындықтан, құрастыру және талдау бір қадам болуы керек, сондықтан өрісті толтырыңыз Таңдамалы сценарий:

PVS-Studio және үздіксіз интеграция: TeamCity. Open RollerCoaster Tycoon 2 жобасын талдау
На отдельных шагах мы остановимся позже. Важно, чтобы загрузка анализатора, сборка проекта, его анализ, вывод отчёта и его форматирование заняло всего одиннадцать строк кода.

Последнее, что нам нужно сделать, — установить переменные окружения, которыми я обозначил некоторые пути для улучшения их читабельности. Для этого перейдём: Параметрлер -> Жаңа параметр қосу и добавим три переменные:

PVS-Studio және үздіксіз интеграция: TeamCity. Open RollerCoaster Tycoon 2 жобасын талдау
Бар болғаны түймені басу керек жүгіру жоғарғы оң жақ бұрышында. Жоба құрастырылып, талданып жатқанда, мен сценарий туралы айтып беремін.

Тікелей сценарий

Біріншіден, біз соңғы PVS-Studio дистрибутивін жүктеп алуымыз керек. Ол үшін Chocolatey пакетінің менеджерін қолданамыз. Бұл туралы көбірек білгісі келетіндер үшін сәйкес бар мақала:

choco install pvs-studio -y

Әрі қарай, CLMonitor жобасын құруды қадағалау қызметтік бағдарламасын іске қосамыз.

%CLmon% monitor –-attach

Содан кейін жобаны орта айнымалысы ретінде құрастырамыз MSB выступает путь к нужной мне для сборки версии 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 There are identical sub-expressions '(1ULL << WIDX_MONTH_BOX)' to the left and to the right of the '|' 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),
  ....
};

Статикалық анализатордан басқа аз адамдар бұл зейінділік сынағынан өте алады. Көшіру-қою үлгісі дәл осы себепті жақсы.

N3 ескертулер

V703 It is odd that the 'flags' field in derived class 'RCT12BannerElement' overwrites field in base class 'RCT12TileElementBase'. Check lines: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570

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

Әрине, негізгі сыныпта және ұрпақта бірдей атпен айнымалыны пайдалану әрқашан қате емес. Дегенмен, мұрагерлік технологияның өзі ата-аналық сыныптың барлық өрістері еншілес сыныпта бар деп болжайды. Мұрагердегі аттас өрістерді жариялау арқылы біз шатасуды тудырамыз.

N4 ескерту

V793 It is odd that the result of the 'imageDirection / 8' statement is a part of the condition. Perhaps, this statement should have been compared with something else. libopenrct2 ObservationTower.cpp 38

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

Толығырақ қарастырайық. Өрнек imageDirection/8 жалған болады, егер imageDirection находится в диапазоне от -7 до 7. Вторая часть: (imageDirection / 8) != 3 чектер imageDirection на нахождение вне диапазона: от -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 жобасын талдау
Егер сіз осы мақаланы ағылшын тілінде сөйлейтін аудиториямен бөліскіңіз келсе, аударма сілтемесін пайдаланыңыз: Владислав Столяров. PVS-Studio және үздіксіз интеграция: TeamCity. Open RollerCoaster Tycoon 2 жобасын талдау.

Ақпарат көзі: www.habr.com

пікір қалдыру