You are on page 1of 17

8/15/2020 PVS-Studio and Continuous Integration: TeamCity.

Analysis of the Open RollerCoaster Tycoon 2 project / PVS-Studio corporate blo…

All streams Development Administrating Design Management Marketing PopSci Log in Sign up

151.42
Rating

PVS-Studio
Static Code Analysis for C, C++, C# and Java

Stolyarrr July 20, 2020 at 05:23 PM

PVS-Studio and Continuous Integration: TeamCity. Analysis of the


Open RollerCoaster Tycoon 2 project
PVS-Studio corporate blog, C++, Game development, DevOps

One of the most relevant scenarios for using the PVS-Studio analyzer is its integration into CI systems. Even though a project analysis by
PVS-Studio can already be embedded with just a few commands into almost any continuous integration system, we continue to make
this process even more convenient. PVS-Studio now supports converting the analyzer output to the TeamCity format-TeamCity
Inspections Type. Let's see how it works.

About the software used


PVS-Studio is a static analyzer of C, C++, C#, and Java code designed to facilitate the task of finding and correcting various types of
errors. The analyzer can be used on Windows, Linux, and macOS. In this article, we will actively use not only the analyzer itself, but also
some utilities from its distribution.

CLMonitor is a monitoring server that performs monitoring of compiler runs. It has to be run immediately before building a project. In
monitoring mode, the server will intercept runs of all supported compilers. It is worth noting that this utility can only be used for
analyzing C/C++ projects.

PlogConverter is a utility for converting the analyzer report into different formats.

About the checked project


Let's try this feature on a practical example by analyzing the OpenRCT2 project.

OpenRCT2 is an open implementation of the RollerCoaster Tycoon 2 (RCT2) game, expanding it with new features and fixed bugs. The
gameplay revolves around the construction and maintenance of an amusement park that houses rides, stores, and facilities. The player
has to try to make profit and maintain the good reputation of the park, while keeping the guests happy. OpenRCT2 allows you to play
both by following the script and in the sandbox. Scenarios require a player to complete a specific task in a set time, while the sandbox
allows a player to build a more flexible park without any restrictions or finances.

Configuration
https://habr.com/en/company/pvs-studio/blog/511770/ 1/17
8/15/2020 PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerCoaster Tycoon 2 project / PVS-Studio corporate blo…

In order to save time, I will probably skip the installation process and start from the point when the TeamCity server is running on my
computer. We need to go to: localhost:{the port specified during installation}(in my case, localhost:9090) and enter the authorization
data. After entering we will get:

Click on Create Project. Next, select Manually and fill in the fields.

https://habr.com/en/company/pvs-studio/blog/511770/ 2/17
8/15/2020 PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerCoaster Tycoon 2 project / PVS-Studio corporate blo…

After clicking Create, we see the window with settings.

Click Create build configuration.

https://habr.com/en/company/pvs-studio/blog/511770/ 3/17
8/15/2020 PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerCoaster Tycoon 2 project / PVS-Studio corporate blo…

Fill in the fields and click Create. We see the window suggesting to select a version control system. Since the sources are already located
locally, click Skip.

https://habr.com/en/company/pvs-studio/blog/511770/ 4/17
8/15/2020 PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerCoaster Tycoon 2 project / PVS-Studio corporate blo…

Finally, we go to the project settings.

https://habr.com/en/company/pvs-studio/blog/511770/ 5/17
8/15/2020 PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerCoaster Tycoon 2 project / PVS-Studio corporate blo…

We'll add the build steps. To do this, click: Build steps -> Add build step.

https://habr.com/en/company/pvs-studio/blog/511770/ 6/17
8/15/2020 PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerCoaster Tycoon 2 project / PVS-Studio corporate blo…

Here we choose:

Runner type -> Command Line

Run -> Custom Script

Since we will perform analysis during the project compilation, the build and analysis must be one step, therefore we will fill in the
Custom Script field:

https://habr.com/en/company/pvs-studio/blog/511770/ 7/17
8/15/2020 PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerCoaster Tycoon 2 project / PVS-Studio corporate blo…

We will focus on individual steps later. It is important that loading the analyzer, building the project, analyzing it, the report output, and
formatting it should take only eleven lines of code.

The last thing we need to do is to set environment variables, which, in my case, outline some ways to improve their readability. To do
this, go to: Parameters -> Add new parameter and add three variables:

https://habr.com/en/company/pvs-studio/blog/511770/ 8/17
8/15/2020 PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerCoaster Tycoon 2 project / PVS-Studio corporate blo…

Just click on Run in the upper-right corner. While the project is being built and analyzed, let me will tell you about the script.

The script itself


First, we need to download the latest PVS-Studio distribution. To do this, we use the Chocolatey package manager. For those who want
to learn more about this, there is a special article:

choco install pvs-studio -y

Next, run the CLMonitor project build monitoring utility.

%CLmon% monitor –-attach

Then we will build the project. The MSB environment variable represents the path to the MSBuild version that I need to build.

%MSB% %ProjPath% /t:clean


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

Enter the username and license key for PVS-Studio:

https://habr.com/en/company/pvs-studio/blog/511770/ 9/17
8/15/2020 PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerCoaster Tycoon 2 project / PVS-Studio corporate blo…

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

After the build is complete, we will run CLMonitor again to generate preprocessed files and perform static analysis:

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

After that, we will use another utility from our distribution. PlogConverter converts a report from standard to a TeamCity-specific format.
This allows us to view it directly in the build window.

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

The last action is to output the formatted report to stdout, where it will be picked up by the TeamCity parser.

type "C:\temp\ptest.plog_TeamCity.txt"

Full script code:

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"

In the meantime, the build and analysis of the project has been completed successfully, so we can go to the Projects tab and make sure
of it.

https://habr.com/en/company/pvs-studio/blog/511770/ 10/17
8/15/2020 PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerCoaster Tycoon 2 project / PVS-Studio corporate blo…

Now click on Inspections Total to view the analyzer report:

https://habr.com/en/company/pvs-studio/blog/511770/ 11/17
8/15/2020 PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerCoaster Tycoon 2 project / PVS-Studio corporate blo…

Warnings are grouped by diagnostic rule numbers. To navigate along the code, click on the line number with the warning. Clicking on
the question mark in the upper-right corner will open a new tab with documentation. You can also navigate along the code by clicking
on the line number with the analyzer warning. Navigation from a remote computer is possible when using the SourceTreeRoot marker.
Those who are interested in this mode of the analyzer operation are welcome to read the related documentation section.

Viewing the analysis results


After we are done with the deployment and configuration of the build, I suggest taking a look at some interesting warnings found in the
reviewed project.

Warning N1

V773 [CWE-401] The exception was thrown without releasing the 'result' pointer. A memory leak is possible. 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)

https://habr.com/en/company/pvs-studio/blog/511770/ 12/17
8/15/2020 PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerCoaster Tycoon 2 project / PVS-Studio corporate blo…
{
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;
}

The analyzer noticed the error that after dynamic memory allocation in CreateObject, when an exception occurs, the memory is not
cleared, and consequently, a memory leak occurs.

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

Few, but a static code analyzer would be able to pass this test for attention. It is diligence that this example of copy paste checks.

Warnings 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
{
....
https://habr.com/en/company/pvs-studio/blog/511770/ 13/17
8/15/2020 PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerCoaster Tycoon 2 project / PVS-Studio corporate blo…
uint8_t flags;
....
};
struct rct1_peep : RCT12SpriteBase
{
....
uint8_t flags;
....
};

Of course, using the same name variable both in the base and derived classes is not always an error. However, inheritance technology
itself assumes that all fields of the parent class are present in the child class. By declaring a field with the same name in the derived
class, we create confusion.

Warning 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)
{
....
}
....
}

Let's look at it in more detail. The imageDirection / 8 expression will be false if imageDirection is in the range from -7 to 7. Second part:
(imageDirection / 8) != 3 checks imageDirection for being outside the range: from -31 to -24 and from 24 to 31, respectively. It seems
rather strange to check numbers for falling into a certain range in this way, and even if there is no error in this code fragment, I would
recommend rewriting these conditions to more explicit ones. This would significantly simplify the lives of people who will read and
maintain this code afterwards.

Warning N5

V587 An odd sequence of assignments of this kind: A = B; B = A;. Check lines: 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;
....
}
....
}

This code fragment was most likely obtained by decompilation. Then, judging by the comment left, a part of the non-working code was
deleted. However, there is still a couple of operations on cursorId that also don't make much sense.
https://habr.com/en/company/pvs-studio/blog/511770/ 14/17
8/15/2020 PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerCoaster Tycoon 2 project / PVS-Studio corporate blo…

Warning N6

V1004 [CWE-476] The 'player' pointer was used unsafely after it was verified against nullptr. Check lines: 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); // <=
}
....
}

This code is quite simple to correct — one either needs to check player for a null pointer for the third time, or add it to the body of the
conditional operator. I would suggest the second option:

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

Warning N7

V547 [CWE-570] Expression 'name == nullptr' is always false. libopenrct2 ServerList.cpp 102

std::optional<ServerListEntry> ServerListEntry::FromJson(...)
{
auto name = json_object_get(server, "name");
.....
if (name == nullptr || version == nullptr)
{
....
}
else

https://habr.com/en/company/pvs-studio/blog/511770/ 15/17
8/15/2020 PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerCoaster Tycoon 2 project / PVS-Studio corporate blo…
{
....
entry.name = (name == nullptr ? "" : json_string_value(name));
....
}
....
}

You can get rid of a hard-to-read line of code in one fell swoop and solve the problem with checking for nullptr. I would change the
code as follows:

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

Warning N8

V1048 [CWE-1164] The 'ColumnHeaderPressedCurrentState' variable was assigned the same value. libopenrct2ui CustomListView.cpp
510

void CustomListView::MouseUp(....)
{
....
TOP POSTS
if (!ColumnHeaderPressedCurrentState)
{
ColumnHeaderPressed = std::nullopt;
Day Week Month
ColumnHeaderPressedCurrentState = false;
Invalidate();
Памятка
} для пострадавшего от слезоточивого газа/перцового баллона
}
+132 47.2k 251 110

TheAudio
code over
looksBluetooth: mostI think
quite strange. detailed information
there was a typo about
either profiles, codecs,or
in the condition and devices
when reassigning the false value to the
ColumnHeaderPressedCurrentStatevariable.
+22 112k 10 8

Conclusion
Technical analysis of the checkm8 exploit
+22 59.6k 5 4
As we can see, it is quite easy to integrate the PVS-Studio static analyzer into your TeamCity project. To do this, you just need to write
one small configuration file. For its part, checking the code will allow you to detect problems immediately after the build, which will help
Java vs .Net: Who will Reign in the Future?
you fix them when the complexity and cost of edits are still small.
+3 6.2k 5 2
Tags: c, c++, static code analysis, static code analyzer, TeamCity, pvs-studio, open source, continious integration, Open RollerCoaster Tycoon,
game development, gamedev

Hubs: PVS-Studio corporate blog, C++, Game development, DevOps


Your account Sections Info Services

–1 1 199 Comment Share


Log in Posts How it works Ads

Sign up Hubs For Authors Subscription plans

https://habr.com/en/company/pvs-studio/blog/511770/ 16/17
8/15/2020 PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerCoaster Tycoon 2 project / PVS-Studio corporate blo…

PVS-Studio Companies For Companies Content


Static Code Analysis for C, C++, C# and Java
Users Documents Seminars

Sandbox Agreement Megaprojects


21.0 4.8 Terms of service
Karma Rating

Vladislav Stolyarov @ Stolyarrr


© 2006 – 2020 «Habr» Language settings About Support Mobile version
Разработчик

Website Twitter VK Instagram Telegram

SIMILAR POSTS

August 6, 2019 at 11:17 AM

Getting Started with the PVS-Studio Static Analyzer for C++ Development under Linux
+21 745 1 0

June 24, 2019 at 10:01 AM

How to quickly check out interesting warnings given by the PVS-Studio analyzer for C and C++ code?
+22 523 1 0

March 11, 2019 at 09:03 PM

Ways to Get a Free PVS-Studio License


+25 2k 4 0

Comments 0

Only users with full accounts can post comments. Log in, please.

https://habr.com/en/company/pvs-studio/blog/511770/ 17/17

You might also like