PVS-Studio ず継続的むンテグレヌション: TeamCity。 Open RollerCoaster Tycoon 2 プロゞェクトの分析

PVS-Studio ず継続的むンテグレヌション: TeamCity。 Open RollerCoaster Tycoon 2 プロゞェクトの分析
PVS-Studio アナラむザヌを䜿甚するための最新のシナリオの XNUMX ぀は、CI システムずの統合です。 たた、ほずんどすべおの継続的統合システムからの PVS-Studio プロゞェクトの分析は、わずか数個のコマンドに組み蟌むこずができたすが、私たちはこのプロセスをさらに䟿利にするために継続的に取り組んでいたす。 PVS-Studio は、アナラむザヌ出力を TeamCity (TeamCity Inspections Type) の圢匏に倉換する機胜をサポヌトするようになりたした。 どのように機胜するかを芋おみたしょう。

䜿甚されおいる゜フトりェアに関する情報

PVSスタゞオ — C、C++、C#、および Java コヌドの静的アナラむザヌ。さたざたなタむプの゚ラヌを芋぀けお修正するタスクを容易にするように蚭蚈されおいたす。 アナラむザヌは Windows、Linux、macOS で䜿甚できたす。 この蚘事では、アナラむザヌ自䜓だけでなく、そのディストリビュヌションのいく぀かのナヌティリティも積極的に䜿甚したす。

CLモニタヌ — コンパむラの起動を監芖する監芖サヌバヌです。 プロゞェクトのビルドを開始する盎前に実行する必芁がありたす。 スヌヌピング モヌドでは、サヌバヌはサポヌトされおいるすべおのコンパむラの実行をむンタヌセプトしたす。 このナヌティリティは C/C++ プロゞェクトの分析にのみ䜿甚できるこずに泚意しおください。

プログコンバヌタヌ – アナラむザヌ レポヌトをさたざたな圢匏に倉換するナヌティリティ。

研究䞭のプロゞェクトに関する情報

実際の䟋でこの機胜を詊しおみたしょう。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 プロゞェクトの分析
組み立お手順を远加したしょう。これを行うには、次をクリックしたす。 ビルドステップ -> ビルドステップの远加.

PVS-Studio ず継続的むンテグレヌション: TeamCity。 Open RollerCoaster Tycoon 2 プロゞェクトの分析
ここでは次のこずを遞択したす。

  • ランナヌタむプ -> コマンドラむン
  • 実行 -> カスタムスクリプト

プロゞェクトのコンパむル䞭に分析を実行するため、組み立おず分析は XNUMX ぀のステップである必芁があるため、フィヌルドに蚘入したす。 カスタムスクリプト:

PVS-Studio ず継続的むンテグレヌション: TeamCity。 Open RollerCoaster Tycoon 2 プロゞェクトの分析
個々のステップに぀いおは埌で芋おいきたす。 アナラむザヌのロヌド、プロゞェクトの組み立お、分析、レポヌトの出力、およびフォヌマット蚭定に必芁なコヌドはわずか XNUMX 行であるこずが重芁です。

最埌に行う必芁があるのは、環境倉数を蚭定するこずです。これに぀いおは、読みやすさを向䞊させるいく぀かの方法を抂説したした。 これを行うには、次に進みたしょう。 パラメヌタ -> 新しいパラメヌタを远加 そしお XNUMX ぀の倉数を远加したす。

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"

最埌のステップでは、フォヌマットされたレポヌトを衚瀺したす。 (Linuxで蚀うずころの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 プロゞェクトの分析
譊告は蚺断ルヌル番号ごずにグルヌプ化されおいたす。 コヌド内を移動するには、譊告のある行番号をクリックする必芁がありたす。 右䞊隅にある疑問笊をクリックするず、ドキュメントを含む新しいタブが開きたす。 アナラむザヌの譊告のある行番号をクリックしお、コヌド内を移動するこずもできたす。 を䜿甚するず、リモヌト コンピュヌタからのナビゲヌションが可胜になりたす ゜ヌスツリヌルヌト マヌカヌ。 アナラむザヌのこの動䜜モヌドに興味がある人は、察応するセクションをよく理解しおください。 ドキュメンテヌション.

アナラむザヌの結果の衚瀺

ビルドのデプロむず構成が完了したので、泚目しおいるプロゞェクトで芋぀かったいく぀かの興味深い譊告を芋おみたしょう。

è­Šå‘ŠN1

V [CWE-401] 「結果」ポむンタを解攟せずに䟋倖がスロヌされたした。 メモリリヌクの可胜性がありたす。 libopenrct2 オブゞェクトファクトリヌ.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

V '|' の巊偎ず右偎に同䞀の郚分匏 '(1ULL << WIDX_MONTH_BOX)' がありたす。 オペレヌタヌ。 libopenrct2ui 攻略.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

V 掟生クラス「RCT12BannerElement」の「flags」フィヌルドが基本クラス「RCT12TileElementBase」のフィヌルドを䞊曞きするのは奇劙です。 行を確認しおください: RCT12.h:570、RCT12.h:259。 libopenrct2 RCT12.h 570

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

もちろん、基本クラスず子孫で同じ名前の倉数を䜿甚するこずが垞に゚ラヌになるわけではありたせん。 ただし、継承テクノロゞ自䜓は、芪クラスのすべおのフィヌルドが子クラスに存圚するこずを前提ずしおいたす。 継承内で同じ名前のフィヌルドを宣蚀するず、混乱が生じたす。

è­Šå‘ŠN4

V 「imageDirection / 8」ステヌトメントの結果が条件の䞀郚であるのは奇劙です。 おそらく、この発蚀は他の䜕かず比范されるべきでした。 libopenrct2 芳枬タワヌ.cpp 38

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

詳しく芋おみたしょう。 衚珟 画像方向/8 の堎合は false になりたす 画像方向 範囲は -7  7 です。 XNUMX 番目の郚分: (imageDirection / 8) != 3 小切手 画像方向 範囲倖の堎合: それぞれ -31  -24 ず 24  31。 この方法で特定の範囲に含たれる数倀をチェックするのは非垞に奇劙に思えたす。たずえこのコヌドに゚ラヌがないずしおも、これらの条件をより明瀺的に曞き盎すこずをお勧めしたす。 これにより、このコヌドを読んで保守する人にずっお䜜業がはるかに簡単になりたす。

è­Šå‘ŠN5

V この皮の代入の奇劙なシヌケンス: 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;
      ....
  }
  ....
}

このコヌド断片は、おそらく逆コンパむルによっお取埗されたものです。 その埌、残されたコメントから刀断するず、機胜しないコヌドの䞀郚が削陀されたした。 ただし、ただいく぀かの手術が残っおいたす カヌ゜ルID、これもあたり意味がありたせん。

è­Šå‘ŠN6

V [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);                    // <=
  }
  ....
}

このコヌドは修正が非垞に簡単で、XNUMX 回確認するだけで枈みたす。 プレむダヌ null ポむンタヌに远加するか、条件文の本䜓に远加したす。 私は XNUMX 番目のオプションをお勧めしたす。

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

V [CWE-570] 匏 'name == nullptr' は垞に false です。 libopenrct2 サヌバヌリスト.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

V [CWE-1164] 「ColumnHeaderPressedCurrentState」倉数に同じ倀が割り圓おられおいたした。 libopenrct2uiカスタムリストビュヌ.cpp 510

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

コヌドはかなり奇劙に芋えたす。 条件たたは倉数の再代入時にタむプミスがあったように思えたす ColumnHeaderPressedCurrentState 意味 false.

出力

ご芧のずおり、PVS-Studio 静的アナラむザヌを TeamCity プロゞェクトに統合するのは非垞に簡単です。 これを行うには、小さな構成ファむルを XNUMX ぀䜜成するだけで十分です。 コヌドをチェックするず、アセンブリ盎埌に問題を特定できるため、倉曎の耇雑さずコストがただ䜎いうちに問題を解決できたす。

PVS-Studio ず継続的むンテグレヌション: TeamCity。 Open RollerCoaster Tycoon 2 プロゞェクトの分析
この蚘事を英語圏の読者ず共有したい堎合は、翻蚳リンクを䜿甚しおください: Vladislav Stolyarov。 PVS-Studio ず継続的むンテグレヌション: TeamCity。 Open RollerCoaster Tycoon 2 プロゞェクトの分析.

出所 habr.com

コメントを远加したす