当前使用 PVS-Studio 分析仪的场景之一是其与 CI 系统的集成。 尽管几乎任何持续集成系统的 PVS-Studio 项目分析都可以构建到几个命令中,但我们继续使这个过程变得更加方便。 PVS-Studio 现在支持将分析器输出转换为 TeamCity - TeamCity 检查类型的格式。 让我们看看它是如何工作的。
有关所使用软件的信息
有关正在研究的项目的信息
让我们在实际示例中尝试此功能 - 让我们分析 OpenRCT2 项目。
调整
为了节省时间,我可能会跳过安装过程,从 TeamCity 服务器在我的计算机上运行的那一刻开始。 我们需要访问: localhost:{安装过程中指定的端口}(在我的例子中为 localhost:9090)并输入授权数据。 进入后,迎接我们的是:
单击“创建项目”按钮。 接下来,选择手动并填写字段。
按下按钮后 创建,我们会看到一个带有设置的窗口。
我们点击一下 创建构建配置.
填写字段并单击 创建。 我们看到一个窗口要求您选择版本控制系统。 由于源已位于本地,因此单击 跳至.
最后,我们继续进行项目设置。
让我们添加组装步骤,为此单击: 构建步骤 -> 添加构建步骤.
这里我们选择:
- 运行器类型 -> 命令行
- 运行 -> 自定义脚本
由于我们会在项目编译时进行分析,所以组装和分析应该是一步,所以填写该字段 自定义脚本:
我们稍后将讨论各个步骤。 重要的是,加载分析器、组装项目、分析它、输出报告并格式化它只需要十一行代码。
我们需要做的最后一件事是设置环境变量,我已经概述了一些提高其可读性的方法。 为此,我们继续: 参数 -> 添加新参数 并添加三个变量:
您所要做的就是按下按钮 运行 在右上角。 在组装和分析该项目的同时,我将向您介绍该脚本。
直接编写脚本
首先,我们需要下载最新的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"
最后一步是在中显示格式化的报告 标准输出,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"
此时,项目的组装和分析已经成功完成,我们可以进入选项卡 项目 и убедиться в этом。
现在我们点击 检查总数转到查看分析器报告:
警告按诊断规则编号分组。 要浏览代码,您需要单击带有警告的行号。 单击右上角的问号将打开一个包含文档的新选项卡。 您还可以通过单击带有分析器警告的行号来浏览代码。 使用时可以从远程计算机进行导航 源树根 标记。 对分析仪的这种操作模式感兴趣的人可以熟悉相应的部分
查看分析仪的结果
现在我们已经完成了构建的部署和配置,让我们看一下我们正在查看的项目中发现的一些有趣的警告。
警告 N1
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;
}
分析器注意到一个错误,在动态分配内存后 创建对象,当异常发生时,内存没有被清除,就会出现内存泄漏。
警告 N2
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
struct RCT12SpriteBase
{
....
uint8_t flags;
....
};
struct rct1_peep : RCT12SpriteBase
{
....
uint8_t flags;
....
};
当然,在基类和后代中使用同名的变量并不总是错误。 然而,继承技术本身假设父类的所有字段都存在于子类中。 通过在继承人中声明具有相同名称的字段,我们会造成混乱。
警告 N4
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
if ((imageDirection / 8) && (imageDirection / 8) != 3)
{
....
}
....
}
让我们仔细看看。 表达 图像方向/8 将为假,如果 图像方向 范围从-7到7。第二部分: (图像方向 / 8) != 3 检查 图像方向 对于超出范围:分别为从-31到-24和从24到31。 对我来说,以这种方式检查数字是否包含在某个范围内似乎很奇怪,即使这段代码没有错误,我也会建议重写这些条件以使其更加明确。 这将使阅读和维护此代码的人的生活变得更加轻松。
警告 N5
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
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
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));
....
}
....
}
您可以一举摆脱一行难以阅读的代码,并通过检查来解决问题 空指针。 我建议更改代码如下:
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
void CustomListView::MouseUp(....)
{
....
if (!ColumnHeaderPressedCurrentState)
{
ColumnHeaderPressed = std::nullopt;
ColumnHeaderPressedCurrentState = false;
Invalidate();
}
}
该代码看起来很奇怪。 在我看来,条件或重新分配变量时存在拼写错误 列标题按下当前状态 意 false.
结论
正如我们所看到的,将 PVS-Studio 静态分析器集成到您的 TeamCity 项目中非常简单。 为此,只需编写一个小配置文件就足够了。 检查代码将使您能够在组装后立即识别问题,这将有助于在更改的复杂性和成本仍然较低时消除问题。
如果您想与英语读者分享这篇文章,请使用翻译链接:Vladislav Stolyarov。
来源: habr.com