Travis CI یک سرویس وب توزیع شده برای ساخت و آزمایش نرم افزار است که از GitHub به عنوان میزبان کد منبع استفاده می کند. علاوه بر سناریوهای عملیاتی فوق، می توانید به لطف گزینه های پیکربندی گسترده، سناریوهای خود را اضافه کنید. در این مقاله ما Travis CI را برای کار با PVS-Studio با استفاده از مثال کد PPSSPP پیکربندی می کنیم.
معرفی
راه اندازی Travis CI
ما به یک مخزن در GitHub نیاز داریم، جایی که پروژه مورد نیاز ما در آن قرار دارد، و همچنین یک کلید برای PVS-Studio (شما می توانید دریافت کنید
بریم تو سایت
برای تست، PPSSPP را فورک کردم.
ما مخزنی را که می خواهیم جمع آوری کنیم فعال می کنیم:
در حال حاضر Travis CI نمی تواند پروژه ما را بسازد زیرا هیچ دستورالعملی برای ساخت وجود ندارد. بنابراین زمان پیکربندی فرا رسیده است.
در طول تجزیه و تحلیل، برخی از متغیرها برای ما مفید خواهند بود، به عنوان مثال، کلید PVS-Studio، که نامطلوب است در فایل پیکربندی مشخص شود. بنابراین بیایید متغیرهای محیطی را با استفاده از تنظیمات ساخت در Travis CI اضافه کنیم:
ما نیاز داریم:
- PVS_USERNAME - نام کاربری
- PVS_KEY - کلید
- MAIL_USER - ایمیلی که برای ارسال گزارش استفاده خواهد شد
- MAIL_PASSWORD - رمز عبور ایمیل
دو مورد آخر اختیاری هستند. اینها برای ارسال نتایج از طریق پست استفاده خواهند شد. اگر می خواهید گزارش را به روش دیگری توزیع کنید، نیازی به نشان دادن آنها ندارید.
بنابراین، ما متغیرهای محیطی مورد نیاز خود را اضافه کرده ایم:
حالا بیایید یک فایل بسازیم .travis.yml و آن را در ریشه پروژه قرار دهید. PPSSPP قبلاً یک فایل پیکربندی برای Travis CI داشت، با این حال، برای مثال بسیار بزرگ و کاملاً نامناسب بود، بنابراین مجبور شدیم آن را تا حد زیادی ساده کنیم و فقط عناصر اصلی را باقی بگذاریم.
ابتدا زبان، نسخه لینوکس اوبونتو که می خواهیم در ماشین مجازی استفاده کنیم و بسته های لازم برای ساخت را مشخص می کنیم:
language: cpp
dist: xenial
addons:
apt:
update: true
packages:
- ant
- aria2
- build-essential
- cmake
- libgl1-mesa-dev
- libglu1-mesa-dev
- libsdl2-dev
- pv
- sendemail
- software-properties-common
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:ubuntu-sdk-team/ppa'
تمام بسته هایی که لیست شده اند منحصراً برای PPSSPP مورد نیاز هستند.
اکنون ماتریس اسمبلی را نشان می دهیم:
matrix:
include:
- os: linux
compiler: "gcc"
env: PPSSPP_BUILD_TYPE=Linux PVS_ANALYZE=Yes
- os: linux
compiler: "clang"
env: PPSSPP_BUILD_TYPE=Linux
کمی بیشتر در مورد بخش ماتریس. در Travis CI، دو راه برای ایجاد گزینه های ساخت وجود دارد: اولی تعیین لیستی از کامپایلرها، انواع سیستم عامل، متغیرهای محیطی و غیره است که پس از آن ماتریسی از تمام ترکیبات ممکن تولید می شود. دومی نشانه صریح ماتریس است. البته، می توانید این دو رویکرد را با هم ترکیب کنید و یک مورد منحصر به فرد اضافه کنید، یا برعکس، آن را با استفاده از بخش حذف کنید. محروم کردن. می توانید در این مورد بیشتر بخوانید
تنها چیزی که باقی می ماند ارائه دستورالعمل های مونتاژ پروژه خاص است:
before_install:
- travis_retry bash .travis.sh travis_before_install
install:
- travis_retry bash .travis.sh travis_install
script:
- bash .travis.sh travis_script
after_success:
- bash .travis.sh travis_after_success
Travis CI به شما اجازه می دهد تا دستورات خود را برای مراحل مختلف زندگی یک ماشین مجازی اضافه کنید. بخش before_install قبل از نصب بسته ها اجرا می شود. سپس نصب، که نصب بسته ها را از لیست دنبال می کند addons.aptکه در بالا اشاره کردیم. خود مونتاژ در آن صورت می گیرد خط. اگر همه چیز خوب پیش رفت، پس خودمان را در آن می یابیم بعد از_موفقیت (در این بخش است که تجزیه و تحلیل استاتیک را اجرا خواهیم کرد). اینها همه مراحلی نیستند که می توان آنها را تغییر داد، اگر به موارد بیشتری نیاز دارید، باید آنها را بررسی کنید
برای سهولت در خواندن، دستورات در یک اسکریپت جداگانه قرار گرفتند تراویس.ش، که در ریشه پروژه قرار می گیرد.
بنابراین ما فایل زیر را داریم .travis.yml:
language: cpp
dist: xenial
addons:
apt:
update: true
packages:
- ant
- aria2
- build-essential
- cmake
- libgl1-mesa-dev
- libglu1-mesa-dev
- libsdl2-dev
- pv
- sendemail
- software-properties-common
sources:
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:ubuntu-sdk-team/ppa'
matrix:
include:
- os: linux
compiler: "gcc"
env: PVS_ANALYZE=Yes
- os: linux
compiler: "clang"
before_install:
- travis_retry bash .travis.sh travis_before_install
install:
- travis_retry bash .travis.sh travis_install
script:
- bash .travis.sh travis_script
after_success:
- bash .travis.sh travis_after_success
قبل از نصب بسته ها، ماژول های فرعی را به روز می کنیم. این برای ساخت PPSSPP مورد نیاز است. بیایید اولین تابع را به آن اضافه کنیم تراویس.ش (به پسوند توجه کنید):
travis_before_install() {
git submodule update --init --recursive
}
اکنون مستقیماً به راهاندازی راهاندازی خودکار PVS-Studio در Travis CI میرسیم. ابتدا باید بسته PVS-Studio را روی سیستم نصب کنیم:
travis_install() {
if [ "$CXX" = "g++" ]; then
sudo apt-get install -qq g++-4.8
fi
if [ "$PVS_ANALYZE" = "Yes" ]; then
wget -q -O - https://files.viva64.com/etc/pubkey.txt
| sudo apt-key add -
sudo wget -O /etc/apt/sources.list.d/viva64.list
https://files.viva64.com/etc/viva64.list
sudo apt-get update -qq
sudo apt-get install -qq pvs-studio
libio-socket-ssl-perl
libnet-ssleay-perl
fi
download_extract
"https://cmake.org/files/v3.6/cmake-3.6.2-Linux-x86_64.tar.gz"
cmake-3.6.2-Linux-x86_64.tar.gz
}
در ابتدای کارکرد travis_install ما کامپایلرهای مورد نیاز خود را با استفاده از متغیرهای محیطی نصب می کنیم. سپس اگر متغیر $PVS_ANALYZE ارزش را ذخیره می کند بله (ما در بخش اشاره کردیم env در طول پیکربندی ماتریس ساخت)، بسته را نصب می کنیم pvs-studio. علاوه بر این، بسته ها نیز نشان داده شده است libio-socket-ssl-perl и libnet-ssleay-perlبا این حال، آنها برای ارسال نتایج مورد نیاز هستند، بنابراین اگر روش دیگری را برای ارائه گزارش خود انتخاب کرده باشید، ضروری نیستند.
تابع دانلود_عصاره بایگانی مشخص شده را دانلود و باز می کند:
download_extract() {
aria2c -x 16 $1 -o $2
tar -xf $2
}
وقت آن است که پروژه را جمع آوری کنیم. این اتفاق در بخش می افتد خط:
travis_script() {
if [ -d cmake-3.6.2-Linux-x86_64 ]; then
export PATH=$(pwd)/cmake-3.6.2-Linux-x86_64/bin:$PATH
fi
CMAKE_ARGS="-DHEADLESS=ON ${CMAKE_ARGS}"
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
cmake $CMAKE_ARGS CMakeLists.txt
make
}
در واقع، این یک پیکربندی اصلی ساده شده است، به جز این خطوط:
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
در این بخش از کد ما برای ساختن پرچم برای صادرات دستورات کامپایل. این برای یک تحلیلگر کد استاتیک ضروری است. شما می توانید در این مورد بیشتر در مقاله بخوانید "
اگر مجمع موفقیت آمیز بود، به آن می رسیم بعد از_موفقیت، جایی که ما تجزیه و تحلیل استاتیک را انجام می دهیم:
travis_after_success() {
if [ "$PVS_ANALYZE" = "Yes" ]; then
pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic
pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic
-o PVS-Studio-${CC}.log
--disableLicenseExpirationCheck
plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html
sendemail -t [email protected]
-u "PVS-Studio $CC report, commit:$TRAVIS_COMMIT"
-m "PVS-Studio $CC report, commit:$TRAVIS_COMMIT"
-s smtp.gmail.com:587
-xu $MAIL_USER
-xp $MAIL_PASSWORD
-o tls=yes
-f $MAIL_USER
-a PVS-Studio-${CC}.log PVS-Studio-${CC}.html
fi
}
بیایید نگاهی دقیق تر به خطوط زیر بیندازیم:
pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic
pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic
-o PVS-Studio-${CC}.log
--disableLicenseExpirationCheck
plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html
خط اول یک فایل مجوز از نام کاربری و کلیدی که در همان ابتدا هنگام تنظیم متغیرهای محیطی Travis CI مشخص کردیم تولید می کند.
خط دوم تحلیل را مستقیماً شروع می کند. پرچم -j تعداد رشته ها را برای تجزیه و تحلیل، پرچم گذاری می کند -l نشان دهنده مجوز، پرچم است -o فایل را برای خروجی گزارشها و پرچم را تعریف میکند -disableLicenseExpirationCheck برای نسخه های آزمایشی مورد نیاز است، زیرا به طور پیش فرض pvs-studio-analyzer به کاربر هشدار می دهد که مجوز در شرف اتمام است. برای جلوگیری از این اتفاق می توانید این پرچم را مشخص کنید.
فایل log حاوی خروجی خام است که بدون تبدیل قابل خواندن نیست، بنابراین ابتدا باید فایل را خوانا کنید. بیایید سیاهههای مربوط را از طریق آن عبور دهیم Plog-Converterو خروجی یک فایل html است.
در این مثال، تصمیم گرفتم با استفاده از دستور، گزارش ها را از طریق پست ارسال کنم ایمیل بفرست.
در نتیجه فایل زیر را دریافت کردیم تراویس.ش:
#/bin/bash
travis_before_install() {
git submodule update --init --recursive
}
download_extract() {
aria2c -x 16 $1 -o $2
tar -xf $2
}
travis_install() {
if [ "$CXX" = "g++" ]; then
sudo apt-get install -qq g++-4.8
fi
if [ "$PVS_ANALYZE" = "Yes" ]; then
wget -q -O - https://files.viva64.com/etc/pubkey.txt
| sudo apt-key add -
sudo wget -O /etc/apt/sources.list.d/viva64.list
https://files.viva64.com/etc/viva64.list
sudo apt-get update -qq
sudo apt-get install -qq pvs-studio
libio-socket-ssl-perl
libnet-ssleay-perl
fi
download_extract
"https://cmake.org/files/v3.6/cmake-3.6.2-Linux-x86_64.tar.gz"
cmake-3.6.2-Linux-x86_64.tar.gz
}
travis_script() {
if [ -d cmake-3.6.2-Linux-x86_64 ]; then
export PATH=$(pwd)/cmake-3.6.2-Linux-x86_64/bin:$PATH
fi
CMAKE_ARGS="-DHEADLESS=ON ${CMAKE_ARGS}"
if [ "$PVS_ANALYZE" = "Yes" ]; then
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
fi
cmake $CMAKE_ARGS CMakeLists.txt
make
}
travis_after_success() {
if [ "$PVS_ANALYZE" = "Yes" ]; then
pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY -o PVS-Studio.lic
pvs-studio-analyzer analyze -j2 -l PVS-Studio.lic
-o PVS-Studio-${CC}.log
--disableLicenseExpirationCheck
plog-converter -t html PVS-Studio-${CC}.log -o PVS-Studio-${CC}.html
sendemail -t [email protected]
-u "PVS-Studio $CC report, commit:$TRAVIS_COMMIT"
-m "PVS-Studio $CC report, commit:$TRAVIS_COMMIT"
-s smtp.gmail.com:587
-xu $MAIL_USER
-xp $MAIL_PASSWORD
-o tls=yes
-f $MAIL_USER
-a PVS-Studio-${CC}.log PVS-Studio-${CC}.html
fi
}
set -e
set -x
$1;
اکنون زمان آن است که تغییرات را در مخزن git اعمال کنید، پس از آن Travis CI به طور خودکار بیلد را اجرا می کند. برای رفتن به گزارش های ساخت بر روی "ppsspp" کلیک کنید:
ما یک نمای کلی از ساخت فعلی را خواهیم دید:
اگر ساخت با موفقیت انجام شود، ایمیلی حاوی نتایج تجزیه و تحلیل استاتیک دریافت خواهیم کرد. البته ارسال پستی تنها راه دریافت گزارش نیست. شما می توانید هر روش پیاده سازی را انتخاب کنید. اما لازم به یادآوری است که پس از اتمام ساخت، دسترسی به فایل های ماشین مجازی امکان پذیر نخواهد بود.
خلاصه خطا
ما سخت ترین قسمت را با موفقیت پشت سر گذاشتیم. حالا بیایید مطمئن شویم که همه تلاش های ما ارزشش را دارد. بیایید به چند نکته جالب از گزارش تجزیه و تحلیل استاتیک که از طریق پست برای من آمده است نگاهی بیاندازیم (بیهوده آن را نشان دادم).
بهینه سازی خطرناک
void sha1( unsigned char *input, int ilen, unsigned char output[20] )
{
sha1_context ctx;
sha1_starts( &ctx );
sha1_update( &ctx, input, ilen );
sha1_finish( &ctx, output );
memset( &ctx, 0, sizeof( sha1_context ) );
}
هشدار PVS-Studio:
این قطعه کد در ماژول هش امن قرار دارد، با این حال، دارای یک نقص امنیتی جدی است (
; Line 355
mov r8d, 20
xor edx, edx
lea rcx, QWORD PTR sum$[rsp]
call memset
; Line 356
همه چیز مرتب است و عملکرد ممست اجرا می شود، در نتیجه داده های مهم را در RAM بازنویسی می کند، با این حال، هنوز خوشحال نشوید. بیایید به لیست اسمبلی نسخه Release با بهینه سازی نگاه کنیم:
; 354 :
; 355 : memset( sum, 0, sizeof( sum ) );
; 356 :}
همانطور که از لیست مشاهده می شود، کامپایلر تماس را نادیده گرفته است ممست. این به دلیل این واقعیت است که در عملکرد sha1 بعد از تماس ممست دیگر اشاره ای به ساختار نیست ctx. بنابراین، کامپایلر هیچ فایده ای در هدر دادن زمان پردازنده برای بازنویسی حافظه ای که در آینده استفاده نمی شود، نمی بیند. با استفاده از تابع می توانید این مشکل را برطرف کنید RtlSecureZeroMemory یا
به درستی:
void sha1( unsigned char *input, int ilen, unsigned char output[20] )
{
sha1_context ctx;
sha1_starts( &ctx );
sha1_update( &ctx, input, ilen );
sha1_finish( &ctx, output );
RtlSecureZeroMemory(&ctx, sizeof( sha1_context ) );
}
مقایسه غیر ضروری
static u32 sceAudioOutputPannedBlocking
(u32 chan, int leftvol, int rightvol, u32 samplePtr) {
int result = 0;
// For some reason, this is the only one that checks for negative.
if (leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || rightvol < 0) {
....
} else {
if (leftvol >= 0) {
chans[chan].leftVolume = leftvol;
}
if (rightvol >= 0) {
chans[chan].rightVolume = rightvol;
}
chans[chan].sampleAddress = samplePtr;
result = __AudioEnqueue(chans[chan], chan, true);
}
}
هشدار PVS-Studio:
برای اول به شاخه else توجه کنید if. کد فقط در صورت وجود تمام شرایط اجرا می شود leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || rightvol < 0 نادرست معلوم خواهد شد بنابراین، عبارات زیر را دریافت می کنیم که برای شاخه else صادق خواهد بود: leftvol <= 0xFFFF, rightvol <= 0xFFFF, leftvol >= 0 и rightvol>= 0. به دو عبارت آخر توجه کنید. آیا بررسی شرط لازم برای اجرای این قطعه کد منطقی است؟
بنابراین می توانیم با خیال راحت این عبارات شرطی را حذف کنیم:
static u32 sceAudioOutputPannedBlocking
(u32 chan, int leftvol, int rightvol, u32 samplePtr) {
int result = 0;
// For some reason, this is the only one that checks for negative.
if (leftvol > 0xFFFF || rightvol > 0xFFFF || leftvol < 0 || rightvol < 0) {
....
} else {
chans[chan].leftVolume = leftvol;
chans[chan].rightVolume = rightvol;
chans[chan].sampleAddress = samplePtr;
result = __AudioEnqueue(chans[chan], chan, true);
}
}
یک سناریوی دیگر. نوعی خطا در پشت این شرایط اضافی پنهان است. شاید آنها آنچه را که لازم بود بررسی نکردند.
Ctrl+C Ctrl+V جواب می دهد
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfData) ||
!Memory::IsValidAddress(psmfData)) {
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
}
....
}
به چک داخل آن دقت کنید if. به نظر شما عجیب نیست که بررسی کنیم آیا آدرس معتبر است؟ psmfData، دو برابر بیشتر؟ بنابراین این برای من عجیب به نظر می رسد... در واقع، این البته یک اشتباه تایپی است و ایده این بود که هر دو پارامتر ورودی را بررسی کنیم.
گزینه صحیح:
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) {
if (!Memory::IsValidAddress(psmfStruct) ||
!Memory::IsValidAddress(psmfData)) {
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address");
}
....
}
متغیر فراموش شده
extern void ud_translate_att(
int size = 0;
....
if (size == 8) {
ud_asmprintf(u, "b");
} else if (size == 16) {
ud_asmprintf(u, "w");
} else if (size == 64) {
ud_asmprintf(u, "q");
}
....
}
هشدار PVS-Studio:
این خطا در پوشه قرار دارد EXT، بنابراین واقعاً مربوط به پروژه نیست، اما اشکال قبل از اینکه متوجه آن شوم پیدا شد، بنابراین تصمیم گرفتم آن را ترک کنم. از این گذشته ، این مقاله در مورد بررسی خطاها نیست ، بلکه در مورد ادغام با Travis CI است و هیچ پیکربندی تحلیلگر انجام نشده است.
متغیر اندازه با یک ثابت مقداردهی اولیه می شود، با این حال، به هیچ وجه در کد استفاده نمی شود، تا اپراتور if، که البته می دهد غلط در حین بررسی شرایط، زیرا همانطور که به یاد داریم، اندازه برابر با صفر بررسی های بعدی نیز بی معنی است.
ظاهراً نویسنده قطعه کد فراموش کرده است که متغیر را بازنویسی کند اندازه قبل از آن.
توقف
اینجاست که احتمالاً با اشتباهات به پایان خواهیم رسید. هدف این مقاله نشان دادن کار PVS-Studio به همراه Travis CI است و نه تجزیه و تحلیل پروژه تا حد امکان. اگر اشتباهات بزرگتر و زیباتر می خواهید، همیشه می توانید آنها را تحسین کنید
نتیجه
استفاده از خدمات وب برای ساخت پروژه ها همراه با تمرین تجزیه و تحلیل افزایشی به شما این امکان را می دهد که بلافاصله پس از ادغام کد، مشکلات زیادی را پیدا کنید. با این حال، ممکن است یک ساخت کافی نباشد، بنابراین تنظیم تست همراه با تجزیه و تحلیل استاتیک به طور قابل توجهی کیفیت کد را بهبود می بخشد.
لینک های مفید
اگر می خواهید این مقاله را با مخاطبان انگلیسی زبان به اشتراک بگذارید، لطفاً از پیوند ترجمه استفاده کنید: Maxim Zvyagintsev.
منبع: www.habr.com