Travis CI هي خدمة ويب موزعة للبناء واختبار البرامج التي تستخدم GitHub كاستضافة شفرة المصدر. بالإضافة إلى السيناريوهات المذكورة أعلاه ، يمكنك إضافة الخاصة بك ، وذلك بفضل خيارات التكوين واسعة النطاق. في هذه المقالة ، سنقوم بتهيئة Travis CI للعمل مع PVS-Studio باستخدام رمز PPSSPP المثال.
مقدمة
Travis CI هي خدمة ويب لبناء واختبار البرامج. وعادة ما تستخدم جنبا إلى جنب مع ممارسة التكامل المستمر.
PPSSPP - محاكي وحدة ألعاب PSP. البرنامج قادر على محاكاة إطلاق أي ألعاب من صور القرص المصممة لسوني PSP. تم إصدار البرنامج في 1 نوفمبر 2012. PPSSPP مرخص بموجب GPL v2. يمكن لأي شخص إجراء تحسينات على
التعليمات البرمجية المصدر للمشروع .
PVS-Studio عبارة عن محلل أكواد ثابت للبحث عن الأخطاء ونقاط الضعف المحتملة في كود البرنامج. في هذه المقالة ، من أجل التغيير ، سنطلق PVS-Studio ليس محليًا على جهاز المطور ، ولكن في السحابة ، ونبحث عن الأخطاء في PPSSPP.
تكوين Travis CI
سوف نحتاج إلى مستودع على GitHub ، حيث المشروع الذي نحتاج إليه يكمن ، وكذلك مفتاح PVS-Studio (يمكنك الحصول على
مفتاح تجريبي أو
مجاني لمشاريع Open Source ).
دعنا نذهب إلى موقع
Travis CI . بعد التفويض باستخدام حساب GitHub ، سيكون لدينا قائمة من المستودعات:
للاختبار ، شوكة PPSSPP.
نقوم بتنشيط المستودع الذي نريد جمعه:
في الوقت الحالي ، لا يمكن Travis CI تجميع مشروعنا ، حيث لا توجد تعليمات للتجميع. لذلك ، حان الوقت للتكوين.
أثناء التحليل ، ستكون بعض المتغيرات مفيدة لنا ، على سبيل المثال ، مفتاح PVS-Studio ، والذي سيكون غير مرغوب فيه لتحديده في ملف التكوين. أضف متغيرات البيئة باستخدام إعدادات الإنشاء في Travis CI:
سنحتاج:
- PVS_USERNAME - اسم المستخدم
- PVS_KEY - مفتاح
- MAIL_USER - البريد الإلكتروني الذي سيتم استخدامه لإرسال التقرير
- MAIL_PASSWORD - كلمة مرور البريد الإلكتروني
الأخيران هما اختياري. سيتم استخدامها لإرسال النتائج عن طريق البريد. إذا كنت ترغب في إرسال التقرير بطريقة أخرى ، فأنت لا تحتاج إلى تحديدها.
لذلك ، أضفنا متغيرات البيئة التي نحتاجها:
الآن قم بإنشاء ملف
.travis.yml ووضعه في جذر المشروع. كان ملف التكوين الخاص بـ Travis CI موجودًا بالفعل في PPSSPP ، ولكنه كان كبيرًا جدًا وغير مناسب تمامًا للمثال ، لذلك اضطررت إلى تبسيطه بشكل كبير وترك العناصر الأساسية فقط.
أولاً ، نشير إلى اللغة ، وإصدار Ubuntu Linux الذي نريد استخدامه في الجهاز الظاهري ، والحزم الضرورية للتجميع:
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 ، هناك طريقتان لإنشاء خيارات الإنشاء: الأولى هي سرد المجمعين ، وأنواع أنظمة التشغيل ، ومتغيرات البيئة ، وما إلى ذلك ، وبعد ذلك يتم إنشاء مصفوفة لجميع المجموعات الممكنة ؛ والثاني هو إشارة واضحة للمصفوفة. بالطبع ، يمكنك دمج هذين النهجين وإضافة حالة فريدة ، أو بدلاً من ذلك ، استبعاد استخدام قسم
الاستبعاد . اقرأ المزيد حول هذا الموضوع
في وثائق 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 ، التي أشرنا إليها أعلاه. التجميع نفسه يحدث في
البرنامج النصي . إذا سارت الأمور على ما يرام ،
فسنصل إلى
after_success (في هذا القسم سنقوم بإجراء تحليل ثابت). هذه ليست جميع الخطوات التي يمكن تعديلها ؛ إذا كنت بحاجة إلى المزيد ، فيجب أن تبحث في
وثائق Travis CI .
لسهولة القراءة ، تم نقل الأوامر إلى برنامج نصي منفصل
.travis.sh ، والذي تم وضعه في جذر المشروع.
لذلك لدينا ملف
.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.sh (
لاحظ الانتباه إلى الامتداد):
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:
في بداية وظيفة
travis_install ، نقوم بتثبيت برامج التحويل البرمجي التي نحتاجها باستخدام متغيرات البيئة. ثم ، إذا قام المتغير
$ PVS_ANALYZE بتخزين القيمة
نعم (حددناها في قسم
env أثناء تكوين مصفوفة التجميع) ، فإننا نقوم بتثبيت حزمة
pvs-studio . بالإضافة إلى ذلك ،
يشار أيضًا إلى حزم
libio-socket-ssl-perl و
libnet-ssleay-perl ، ومع ذلك ، فهناك حاجة لإرسال النتائج عبر البريد ، لذا فهي غير ضرورية إذا اخترت طريقة مختلفة لتسليم التقرير.
تقوم وظيفة download_extract بتنزيل الأرشيف المحدد وتفريغه:
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
في هذا القسم من التعليمات البرمجية ، قمنا بتعيين علم تصدير أوامر
الترجمة لـ
cmake . هذا ضروري لمحلل كود ثابت. يمكنك قراءة المزيد حول هذا الموضوع في المقالة "
كيفية تشغيل PVS-Studio على Linux و macOS ".
إذا نجحت المجموعة ، فسنجد أنفسنا بعد
النجاح ، حيث نجري تحليلًا ثابتًا:
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 mail@domain.com \ -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 <N> عدد التدفقات للتحليل ، وتشير العلامة
-l <file> إلى الترخيص ، وتعرّف العلامة
-o <file> الملف الخاص بإخراج السجل ، وتكون العلامة
-disableLicenseExpirationCheck للإصدارات التجريبية ، نظرًا لأن
pvs الافتراضي
سوف يحذر
studio-analys المستخدم من انتهاء الترخيص. لمنع هذا ، يمكنك تحديد هذه العلامة.
يحتوي ملف السجل على إخراج أولي لا يمكن قراءته بدون تحويل ، لذلك يجب أولاً جعل الملف قابلاً للقراءة.
نتخطى السجلات من خلال
محول plog ، والإخراج هو ملف html.
في هذا المثال ، قررت إرسال التقارير عن طريق البريد باستخدام أمر
sendemail .
نتيجة لذلك ، حصلنا على ملف
.travis.sh التالي:
#/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:
لقد حان الوقت لإضافة التغييرات إلى مستودع 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:
V597 يمكن للمترجم حذف استدعاء دالة "memset" ، والذي يُستخدم لمسح "المخزن المؤقت" المؤقت. يجب استخدام الدالة RtlSecureZeroMemory () لمسح البيانات الخاصة. sha1.cpp 325
يوجد جزء الكود هذا في وحدة التجزئة الآمنة ، ومع ذلك ، فإنه يحتوي على ثغرة أمنية خطيرة (
CWE-14 ). خذ بعين الاعتبار قائمة المجمّع التي يتم إنشاؤها عند ترجمة إصدار Debug:
; Line 355 mov r8d, 20 xor edx, edx lea rcx, QWORD PTR sum$[rsp] call memset ; Line 356
كل شيء في حالة ممتازة ، ويتم تنفيذ وظيفة
memset ، وبالتالي الكتابة فوق البيانات المهمة في ذاكرة الوصول العشوائي ، ومع ذلك ، لا نفرح حتى الآن. النظر في قائمة المجمع من الإصدار الإصدار مع التحسين:
; 354 : ; 355 : memset( sum, 0, sizeof( sum ) ); ; 356 :}
كما يتضح من القائمة ، تجاهل المترجم استدعاء
memset . هذا لأن الدالة
sha1 لم تعد تستدعي بنية
ctx بعد استدعاء
memset . لذلك ، لا يرى المحول البرمجي نقطة تضييع وقت المعالج في الكتابة فوق الذاكرة غير المستخدمة في المستقبل. يمكنك إصلاح ذلك باستخدام وظيفة
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;
تحذير PVS-Studio: التعبير
V547 'leftvol> = 0' صحيح دائمًا. sceAudio.cpp 120
إيلاء الاهتمام لفرع آخر لأول مرة
إذا . سيتم تنفيذ الكود فقط إذا تم
ترك جميع الشروط
> 0xFFFF || rightvol> 0xFFFF || leftvol <0 || rightvol <0 سوف يتحول إلى خطأ. لذلك ، نحصل على العبارات التالية التي ستكون صحيحة للفرع الآخر:
leftvol <= 0xFFFF ،
rightvol <= 0xFFFF ،
leftvol> = 0 و
rightvol> = 0 . إيلاء الاهتمام لآخر بيانين. هل من المنطقي التحقق من الشرط المسبق لتنفيذ هذه الشفرة؟
حتى نتمكن من إزالة هذه العبارات الشرطية بهدوء:
static u32 sceAudioOutputPannedBlocking (u32 chan, int leftvol, int rightvol, u32 samplePtr) { int result = 0;
سيناريو آخر. وراء هذه الظروف الزائدة هو نوع من الخطأ. ربما فحصوا ليس ما هو مطلوب.
Ctrl + C Ctrl + V Strikes Back
static u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData) { if (!Memory::IsValidAddress(psmfData) || !Memory::IsValidAddress(psmfData)) { return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad address"); } .... }
V501 هناك تعبيرات فرعية متطابقة '! Memory :: IsValidAddress (psmfData)' إلى اليسار وإلى يسار '||' المشغل. scePsmf.cpp 703
إيلاء الاهتمام للتحقق في الداخل
إذا . ألا يبدو غريباً أننا نتحقق مما إذا كان العنوان
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:
V547 Expression 'size == 8' غير صحيح دائمًا. syn-att.c 195
يوجد هذا الخطأ في مجلد
ext ، لذلك لا ينطبق تمامًا على المشروع ، ولكن تم العثور على الخطأ قبل أن لاحظت ذلك ، لذلك قررت تركه. لا يزال ، هذا المقال لا يتعلق بمراجعة الأخطاء ، ولكن عن التكامل مع Travis CI ، ولم يتم إجراء أي تكوين للمحلل.
تتم تهيئة
حجم متغير بواسطة ثابت ، ومع ذلك ، لا يتم استخدامه على الإطلاق في التعليمات البرمجية ، حتى
العبارة if ، والتي ، بطبيعة الحال ، ترجع
خطأ عند التحقق من الشرط ، لأن
الحجم ، كما نذكر ، هو صفر. الشيكات اللاحقة لا معنى لها كذلك.
على ما يبدو ، نسي مؤلف جزء الشفرة الكتابة فوق
الحجم المتغير قبل ذلك.
توقف
على هذا ، ربما ، نحن في نهاية المطاف مع الأخطاء. الغرض من هذه المقالة هو إظهار عمل PVS-Studio بالتزامن مع Travis CI ، وليس لتحليل المشروع على أكمل وجه ممكن. إذا كنت تريد المزيد والمزيد من الأخطاء الجميلة ، فيمكنك دائمًا الإعجاب بها
هنا :).
استنتاج
يمكن أن يؤدي استخدام خدمات الويب لإنشاء مشاريع إلى جانب ممارسة التحليل التزايدي إلى اكتشاف العديد من المشكلات فور دمج الكود. صحيح أن التجميع الفردي قد لا يكون كافيًا ، لذا فإن إعداد الاختبار مع التحليل الثابت سيحسن بشكل كبير من جودة الشفرة.
روابط مفيدة

إذا كنت ترغب في مشاركة هذه المقالة مع جمهور يتحدث الإنجليزية ، فالرجاء استخدام الرابط الخاص بالترجمة: Maxim Zvyagintsev.
كيفية إعداد PVS-Studio في Travis CI باستخدام مثال محاكي وحدة ألعاب PSP .