云中的PVS-Studio:GitLab CI / CD

图2

本文继续介绍有关在云系统中使用PVS-Studio的系列出版物。 这次,我们将探讨分析仪与GitLab CI(由GitLab Inc.生产的产品)一起工作的方式。 CI系统中的静态分析器集成允许在项目构建后立即检测错误,这是降低查找错误成本的高效方法。

我们有关集成到云CI系统中的其他文章的列表:


有关所用软件的信息


GitLab是旨在管理存储库的在线服务。 您可以通过注册帐户在官方网站的浏览器中直接使用它,或在您自己的服务器上安装和部署它。

PVS-Studio是一种工具,旨在检测用C,C ++,C#和Java编写的程序源代码中的错误和潜在漏洞。 可在Windows,Linux和macOS上的64位系统上运行,并可分析32位,64位和嵌入式ARM平台的代码。 如果这是您第一次使用分析器检查项目,我们建议您阅读有关如何快速检查出最有趣的PVS-Studio警告并评估工具功能的文章。

OBS项目将用于演示云中静态分析器的功能。 Open Broadcaster Software是一套免费开放的程序,用于视频录制和流传输。 OBS提供实时的设备和源拦截,场景合成,解码,记录和广播。 数据主要通过实时消息协议进行传输,并且可以发送到任何支持RTMP的源-该程序具有现成的预安装件,可以在最流行的流媒体平台上进行实时广播。

构型


要开始使用GitLab,请访问网站并单击注册

图6

您可以通过链接其他服务(例如GitHub,Twitter,Google,BitBucket,Saleforce)的帐户进行注册,也可以直接填写开放表格进行注册。 授权后,GitLab邀请我们创建一个项目:

图7

可用平台来导入文件的列表:

图33


为了清楚起见,我们创建一个空项目:

图17


接下来,我们需要将项目上传到创建的存储库中。 使用创建的项目窗口中显示的提示进行操作。

图1


启动任务时,GitLab CI从.gitlab-ci.yml文件中获取指令。 您可以通过单击“ 设置CI / CD ”或仅通过创建本地存储库并将其上载到站点来添加它。 让我们遵循第一个选项:

图21



现在为脚本做一个最小的包装:

image: debian job: script: 

下载分析器和sendemail实用程序,稍后我们将需要它:

 - apt-get update && apt-get -y install wget gnupg - wget -O - https://files.viva64.com/etc/pubkey.txt | apt-key add - - wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - apt-get update && apt-get -y install pvs-studio sendemail 

接下来,我们将安装用于构建OBS的依赖项和实用程序:

 - apt-get -y install build-essential cmake make pkg-config libx11-dev libgl1-mesa-dev libpulse-dev libxcomposite-dev libxinerama-dev libv4l-dev libudev-dev libfreetype6-dev libfontconfig-dev qtbase5-dev libqt5x11extras5-dev libx264-dev libxcb-xinerama0-dev libxcb-shm0-dev libjack-jackd2-dev libcurl4-openssl-dev libavcodec-dev libqt5svg5 libavfilter-dev libavdevice-dev libsdl2-dev ffmpeg qt5-default qtscript5-dev libssl-dev qttools5-dev qttools5-dev-tools qtmultimedia5-dev libqt5svg5-dev libqt5webkit5-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-jackd2-dev libxrandr-dev libqt5xmlpatterns5-dev libqt5xmlpatterns5 coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls28-dev libselinux1-dev linux-libc-dev libtool autotools-dev libio-socket-ssl-perl libnet-ssleay-perl ca-certificates 

现在,我们需要使用分析器许可证创建文件(默认情况下,将在目录../.config/PVS-Studio中创建文件PVS-Studio.lic)。 这样,您不必在分析仪的运行参数中指定许可证文件,它会自动被捕获):

 - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY 

PVS_NAMEPVS_KEY是变量的名称,我们在设置中指定了变量的值。 他们将存储PVS-Studio登录名和许可证密钥。 要设置它们的值,请遵循:设置> CI / CD>变量。

图23


使用cmake构建项目:

 - cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=On /builds/Stolyarrrov/obscheck/ - make -j4 

之后,运行分析器:

 - pvs-studio-analyzer analyze -o PVS-Studio.log 

PVS-Studio.log将存储分析结果。 包含报告的结果文件不适合读取。 为了使人眼可访问,我们需要plog-converter实用程序。 该程序将分析仪的日志转换为不同的格式。 为了便于阅读,让我们将日志转换为html格式:

 - plog-converter -t html PVS-Studio.log -o PVS-Studio.html 

您可以使用artifacts导出报告,但我们将更改方法,并使用sendemail实用程序通过电子邮件通过分析器结果发送文件:

 - sendemail -t $MAIL_TO -u "PVS-Studio report, commit:GITLAB_COMMIT" -m "PVS-Studio report, commit:GITLAB_COMMIT" -s $GMAIL_PORT -o tls=auto -f $MAIL_FROM -xu $MAIL_FROM -xp $MAIL_FROM_PASS -a PVS-Studio.log PVS-Studio.html 

完整的.gitlab-ci.yml:

 image: debian job: script: - apt-get update && apt-get -y install wget gnupg - wget -O - https://files.viva64.com/etc/pubkey.txt | apt-key add - - wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - apt-get update && apt-get -y install pvs-studio sendemail - apt-get -y install build-essential cmake pkg-config libx11-dev libgl1-mesa-dev libpulse-dev libxcomposite-dev libxinerama-dev libv4l-dev libudev-dev libfreetype6-dev libfontconfig-dev qtbase5-dev libqt5x11extras5-dev libx264-dev libxcb-xinerama0-dev libxcb-shm0-dev libjack-jackd2-dev libcurl4-openssl-dev libavcodec-dev libqt5svg5 libavfilter-dev libavdevice-dev libsdl2-dev ffmpeg qt5-default qtscript5-dev libssl-dev qttools5-dev qttools5-dev-tools qtmultimedia5-dev libqt5svg5-dev libqt5webkit5-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-jackd2-dev libxrandr-dev libqt5xmlpatterns5-dev libqt5xmlpatterns5 coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls28-dev libselinux1-dev linux-libc-dev libtool autotools-dev make libio-socket-ssl-perl libnet-ssleay-perl ca-certificates - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY - cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=On /builds/Stolyarrrov/obscheck/ - make -j4 - pvs-studio-analyzer analyze -o PVS-Studio.log - plog-converter -t html PVS-Studio.log -o PVS-Studio.html - sendemail -t $MAIL_TO -u "PVS-Studio report, commit:GITLAB_COMMIT" -m "PVS-Studio report, commit:GITLAB_COMMIT" -s $GMAIL_PORT -o tls=auto -f $MAIL_FROM -xu $MAIL_FROM -xp $MAIL_FROM_PASS -a PVS-Studio.log PVS-Studio.html 

单击提交更改 。 如果一切正确,我们将看到输出: 此GitLab CI配置有效。 要跟踪进度,让我们转到标签CI / CD> Pipelines

图5


点击运行 。 我们将看到运行配置文件的虚拟机终端窗口。 一段时间后,我们收到一条消息: 作业成功。

图29


因此,该打开带有通过邮件发送的警告的html文件了。

分析结果


让我们看一下报告中的一些警告,揭示Open Broadcaster Software项目中的错误,以便获得静态代码分析的本质。 由于本文的主要目的是描述PVS-Studio和GitLab CI / CD交互的原理,因此仅举了几个简单的例子。 我们准备为该项目的作者提供临时许可证,如果愿意,欢迎他们进行更彻底的项目分析。 此外,他们可以使用其中一种方法来获取免费的PVS-Studio许可证

每个人都可以获得试用钥匙,以探索PVS-Studio功能并检查其项目。

因此,让我们注意一些在Open Broadcaster Software中发现的错误的示例。

警告N1

V547表达式“ back_size”始终为true。 circlebuf.h(138)

 struct circlebuf { .... size_t capacity; }; static inline void circlebuf_place(struct circlebuf *cb, size_t position,....,const void *data, size_t size) { .... size_t data_end_pos; data_end_pos = position + size; if (data_end_pos > cb->capacity) { size_t back_size = data_end_pos - cb->capacity; if (back_size) { memcpy((uint8_t *)cb->data + position, data, loop_size); } .... } 

if(data_end_pos> cb->容量)这一行绝对值得仔细研究。 如果条件为真,则在下面的行中定义的back_size变量将始终大于零,因为在这里,我们处理的是将臭名昭著的较小值减去较大的值。 最后,下面两行的条件将始终为true 。 冗余条件并不是紧随其后的是代码,即更改数据。

警告N2,N3

V629考虑检查'1 <<缩进'表达式。 32位值的位移,随后扩展为64位类型。 profiler.c(610)

 static void profile_print_entry(uint64_t active, unsigned indent, ....) { .... active &= (1 << indent) - 1; .... } 

在这里,对32位和64位类型的混淆操作看起来很可疑。 首先,程序员使用32位类型(表达式(1 << indent)-1 )评估掩码,然后隐式扩展为active&= ...表达式中的64位类型。 最有可能在评估掩码时,还需要使用64位类型。

正确的代码版本:

 active &= ((uint64_t)(1) << indent) - 1; 

或者:

 active &= (1ull << indent) - 1; 

顺便说一下,此块的复制粘贴版本在下面,分析器也为此发出警告: V629考虑检查'1 << indent'表达式。 32位值的位移,随后扩展为64位类型。 profiler.c(719)

警告N4

V761找到四个相同的文本块。 'obs-audio-controls.c'(353)

 static float get_true_peak(....) { .... peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); .... } 

四个相同的块。 几乎在每种情况下,此类代码都表示复制粘贴错误。 最有可能的是,必须使用不同的参数调用这些函数。 即使没有,此代码看起来也很奇怪。 一个好的解决方案是只编写一次该块并将其包装在循环中:

 for(size_t i = 0; i < 3; i++) { peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); } 

警告n5

V560条件表达式的一部分始终为false:“!修饰符”。 obs-hotkey.c(662)

 typedef struct obs_key_combination obs_key_combination_t; struct obs_key_combination { uint32_t modifiers; obs_key_t key; }; static inline void load_binding(....) { obs_key_combination_t combo = {0}; uint32_t *modifiers = &combo.modifiers; load_modifier(modifiers, data, "shift", INTERACT_SHIFT_KEY); load_modifier(modifiers, data, "control", INTERACT_CONTROL_KEY); load_modifier(modifiers, data, "alt", INTERACT_ALT_KEY); load_modifier(modifiers, data, "command", INTERACT_COMMAND_KEY); if (!modifiers && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE)) { .... } .... } 

load_modifier函数的定义:

 static inline void load_modifier(uint32_t *modifiers, obs_data_t *data, const char *name, uint32_t flag) { if (obs_data_get_bool(data, name)) *modifiers |= flag; } 

如我们所见, 修饰符是一个指针,由组合结构的修饰符字段的地址初始化。 由于其值在检查之前不会改变,因此将保持非空值。 而且,在检查之前的初始化之后,在调用load_modifier函数时会使用指针,并在此取消引用。 相应地, !修饰符检查是没有意义的,因为&&运算符使我们在评估逻辑表达式时总会得到false 。 我认为程序员想通过存储在修饰符指针中的地址来检查一个整数值,但是却忘记了对该指针的取消引用。

所以在我看来,支票应如下所示:

 if (!*modifiers && ....) Or like this: if (!combo.modifiers && ....) 

警告N6

V575潜在的空指针被传递给'strncpy'函数。 检查第一个参数。 检查线:2904、2903。rtmp.c(2904)

 static int PublisherAuth(....) { .... ptr = malloc(r->Link.app.av_len + pubToken.av_len); strncpy(ptr, r->Link.app.av_val, r->Link.app.av_len); .... } 

通常,此类代码是不安全的,因为它忽略了malloc可以返回空指针的情况。 如果malloc返回NULL ,则在这种情况下将发生未定义的行为,因为strncpy函数的第一个参数将具有NULL值。

有关检查malloc函数的返回值为何重要的更多信息,请参阅相关文章

警告N7,N8,N9

猜测哪些情况包含错误的计算:

 class OBSProjector : public OBSQTDisplay { .... float sourceX, sourceY, ....; .... } .... void OBSProjector::OBSRenderMultiview(....) { OBSProjector *window = (OBSProjector *)data; .... auto calcBaseSource = [&](size_t i) { switch (multiviewLayout) { case MultiviewLayout::HORIZONTAL_TOP_24_SCENES: window->sourceX = (i % 6) * window->scenesCX; window->sourceY = window->pvwprgCY + (i / 6) * window->scenesCY; break; case MultiviewLayout::VERTICAL_LEFT_8_SCENES: window->sourceX = window->pvwprgCX; window->sourceY = (i / 2) * window->scenesCY; if (i % 2 != 0) { window->sourceX += window->scenesCX; } break; case MultiviewLayout::VERTICAL_RIGHT_8_SCENES: window->sourceX = 0; window->sourceY = (i / 2) * window->scenesCY; if (i % 2 != 0) { window->sourceX = window->scenesCX; } break; case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = 0; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->scenesCY; } break; default:// MultiviewLayout::HORIZONTAL_TOP_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = window->pvwprgCY; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->pvwprgCY + window->scenesCY; } } } .... } 

分析仪警告:

  • V636'i / 6'表达式从'size_t'类型隐式转换为'float'类型。 考虑使用显式类型转换以避免丢失小数部分。 例如:double A =(double)(X)/ Y;。 window-projector.cpp(330)
  • V636'i / 2'表达式从'size_t'类型隐式转换为'float'类型。 考虑使用显式类型转换以避免丢失小数部分。 例如:double A =(double)(X)/ Y;。 window-projector.cpp(334)
  • V636'i / 2'表达式从'size_t'类型隐式转换为'float'类型。 考虑使用显式类型转换以避免丢失小数部分。 例如:double A =(double)(X)/ Y;。 window-projector.cpp(340)

这是正确的答案:在那些答案中, 不会被迫浮动。 分析器向我们显示了具有整数除法的片段。 这样的代码可能无法以程序员希望的方式工作。

结论


如我们所见,在您的GitLab项目中集成PVS-Studio分析仪是一个非常简单的过程。 为此,您只需要编写一个配置文件并将其放置在云存储库中即可。 由于GitLab具有自己的集成虚拟机,因此,我们甚至不需要花费大量时间来配置CI系统。 代码检查将使您在构建后立即发现问题。 这有助于在其复杂性和成本还很小的阶段消除问题。

Source: https://habr.com/ru/post/zh-CN476626/


All Articles