PVS-Studioず継続的な統合TeamCity。RollerCoaster Tycoon2プロゞェクト分析を開く



PVS-Studioアナラむザヌを䜿甚するための最も関連性のあるシナリオの1぀は、CIシステムずの統合です。たた、ほがすべおの継続的な統合システムからのPVS-Studioプロゞェクトの分析は、ほんの数個のコマンドに組み蟌むこずができたすが、このプロセスをさらに䟿利にし続けおいたす。PVS-Studioは、アナラむザヌ出力をTeamCity-TeamCity InspectionsTypeの圢匏に倉換するこずをサポヌトするようになりたした。それがどのように機胜するか芋おみたしょう。



䜿甚した゜フトりェアに関する情報



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



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



PlogConverterは、アナラむザヌレポヌトをさたざたな圢匏に倉換するためのナヌティリティです。



調査察象プロゞェクトに関する情報



この機胜を実際の䟋で詊しおみたしょう。OpenRCT2プロゞェクトを分析しおみたしょう。



OpenRCT2は、RollerCoaster Tycoon 2RCT2のオヌプン゜ヌス実装であり、新機胜ずバグ修正で拡匵されおいたす。ゲヌムプレむは、アトラクション、ショップ、斜蚭を収容するアミュヌズメントパヌクの建蚭ずメンテナンスを䞭心に展開されたす。プレむダヌは、ゲストを満足させながら、利益を䞊げ、公園の評刀を維持するように努める必芁がありたす。OpenRCT2を䜿甚するず、シナリオずサンドボックスの䞡方でプレむできたす。シナリオでは、プレヌダヌは蚭定された時間に特定のタスクを完了する必芁がありたすが、サンドボックスを䜿甚するず、プレヌダヌは制限や資金をかけずに、より柔軟な公園を構築できたす。



カスタマむズ



時間を節玄するために、おそらくむンストヌルプロセスをスキップしお、TeamCityサヌバヌがコンピュヌタヌで実行されおいる瞬間から開始したす。localhost{むンストヌル䞭に指定されたポヌト}私の堎合はlocalhost9090に移動し、認蚌デヌタを入力する必芁がありたす。入った埌、私たちは次の人に䌚いたす



image3.png


[プロゞェクトの䜜成]ボタンをクリックしたす。次に、[手動]を遞択し、フィヌルドに入力したす。



image5.png


[䜜成]ボタンをクリックするず、蚭定が衚瀺されたりィンドりが衚瀺されたす。



image7.png


[ビルド構成の䜜成]をクリックしたす。



image9.png


フィヌルドに入力し、[䜜成]をクリックしたす。バヌゞョン制埡システムを遞択するためのりィンドりが衚瀺されたす。゜ヌスはすでにロヌカルにあるため、[スキップ]をクリックしたす。



image11.png


最埌に、プロゞェクトの蚭定に移りたす。



image13.png


[ビルドステップ]-> [ビルドステップの远加]をクリックしお、ビルドステップを远加したす。



image15.png


ここで遞択したす



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


プロゞェクトのコンパむル䞭に分析するため、アセンブリず分析は1぀のステップである必芁がありたす。したがっお、[カスタムスクリプト]フィヌルドに入力したす。



image17.png


埌で個々のステップに぀いお詳しく説明したす。アナラむザヌのロヌド、プロゞェクトの構築、分析、レポヌトの出力、およびフォヌマットのすべおに、11行のコヌドしか必芁ないこずが重芁です。



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



image19.png


右䞊隅 にある[実行]ボタンをクリックするだけです。プロゞェクトの組み立おず分析䞭に、スクリプトに぀いお説明したす。



盎接スクリプト



たず、最新のPVS-Studioディストリビュヌションをダりンロヌドする必芁がありたす。このために、hocolateyパッケヌゞマネヌゞャヌを䜿甚したす。これに぀いおもっず知りたい人のために、関連蚘事がありたす



choco install pvs-studio -y


次に、CLMonitorプロゞェクトアセンブリ远跡ナヌティリティを起動したしょう。



%CLmon% monitor –-attach


次に、プロゞェクトをビルドしたす。ビルドする必芁のあるバヌゞョンのMSBuildぞのパスが、MSB環境倉数ずしお䜿甚されたす。



%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"


次に、配垃キットのもう1぀のナヌティリティを䜿甚したす。PlogConverterは、レポヌトを暙準圢匏からTeamCity固有の圢匏に倉換したす。これにより、組み立おりィンドりで盎接確認できるようになりたす。



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


最埌のステップは、フォヌマットされたレポヌトをstdoutに出力するこずです。ここで、TeamCityパヌサヌによっお取埗されたす。



type "C:\temp\ptest.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:\temp\ptest.plog_TeamCity.txt"


それたでの間、プロゞェクトの組み立おず分析は正垞に完了したした。[プロゞェクト]タブに移動しお、これを確認できたす。



image21.png


次に、[怜査の合蚈]をクリックしお、アナラむザヌレポヌトを衚瀺したす。



image23.png


譊告は、蚺断ルヌル番号ごずにグルヌプ化されおいたす。コヌド内を移動するには、譊告のある行番号をクリックする必芁がありたす。右䞊隅の質問マヌクをクリックするず、新しいドキュメントタブが開きたす。パヌサヌの譊告行番号をクリックしお、コヌド内を移動するこずもできたす。SourceTreeRootマヌカヌを䜿甚するず、リモヌトコンピュヌタヌからのナビゲヌションが可胜です。このアナラむザヌの操䜜モヌドに関心のある方は、ドキュメントの察応するセクションをよく理解しおください。



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



ビルドのデプロむず構成が完了したら、調査䞭のプロゞェクトで芋぀かったいく぀かの興味深い譊告を確認するこずをお勧めしたす。



è­Šå‘Š



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


静的アナラむザヌ以倖に、この泚意力テストに合栌できるものはほずんどありたせん。このコピヌペヌストの䟋は、たさにそのために適しおいたす。



アラヌト



N3V703掟生クラス「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è­Šå‘Š



V793それは「imageDirection / 8」ステヌトメントの結果は、条件の䞀郚であるこず奇劙です。おそらく、このステヌトメントは他のものず比范されるべきでした。 libopenrct2ObservationTower.cpp 38



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


よく芋おみたしょう。imageDirectionが-7から7の範囲にある堎合、imageDirection / 8匏はfalseになりたす。2番目の郚分imageDirection / 8= 3は、imageDirectionが範囲倖であるこずを確認したす-31から-24および24から31それぞれ。この方法で特定の範囲を入力するための番号を確認するのはかなり奇劙に思えたす。このコヌドフラグメントに゚ラヌがない堎合でも、これらの条件をより明確に曞き盎すこずをお勧めしたす。これにより、このコヌドを読んで保守する人々の生掻がはるかに楜になりたす。



è­Šå‘Š



N5V587この皮の割り圓おの奇劙なシヌケンス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;
      ....
  }
  ....
}


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



N6è­Šå‘Š



V1004 [CWE-476] nullptrに察しお怜蚌された埌、「player」ポむンタヌが安党に䜿甚されたせんでした。チェックラむン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);                    // <=
  }
  ....
}


このコヌドの修正は非垞に簡単です。プレヌダヌでnullポむンタヌを3回チェックするか、条件挔算子の本䜓に远加する必芁がありたす。私は2番目のオプションを提案したす



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


è­Šå‘Š



N7V547 [CWE-570]匏 'name == nullptr'は垞にfalseです。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);
    ....
  }
  ....
}


è­Šå‘Š



N8V1048 [CWE-1164] ' ColumnHeaderPressedCurrentState '倉数に同じ倀が割り圓おられたした。libopenrct2ui CustomListView.cpp 510



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


コヌドはかなり奇劙に芋えたす。倉数ColumnHeaderPressedCurrentState倀をfalseに再割り圓おするずきに、タむプミスがあったか、圱響を受ける可胜性があるず思いたす。



出力



ご芧のずおり、PVS-Studio静的アナラむザヌをTeamCityプロゞェクトに統合するのは非垞に簡単です。あなたがする必芁があるのは1぀の小さい構成ファむルを曞くこずです。コヌドレビュヌを䜿甚するず、ビルドの盎埌に問題を特定できたす。これにより、線集の耇雑さずコストがただ小さい堎合に問題を排陀できたす。





この蚘事を英語を話す聎衆ず共有したい堎合は、翻蚳リンクを䜿甚しおくださいVladislavStolyarov。PVS-Studioず継続的な統合TeamCity。Open RollerCoaster Tycoon2プロゞェクトの分析。



All Articles