Travis CI هي خدمة ويب موزعة لبناء واختبار البرامج التي تستخدم GitHub كخدمة استضافة التعليمات البرمجية المصدر. بالإضافة إلى البرامج النصية أعلاه ، يمكنك إضافة الخاصة بك ، وذلك بفضل خيارات التكوين واسعة النطاق. في هذه المقالة ، سنقوم بإعداد Travis CI للعمل مع PVS-Studio بمثال رمز PPSSPP.
مقدمة
Travis CI هي خدمة ويب لبناء واختبار البرامج. وعادة ما تستخدم في تركيبة مع ممارسة التكامل المستمر.
PPSSPP هو محاكي وحدة ألعاب PSP. البرنامج قادر على محاكاة إطلاق أي لعبة بصور أقراص مصممة لسوني PSP. تم إصدار البرنامج في 1 نوفمبر 2012. يتم توزيع PPSSPP بموجب ترخيص GPL v2. يمكن لأي شخص إجراء تحسينات على
التعليمات البرمجية المصدر للمشروع.
PVS-Studio - محلل الكود الثابت للبحث عن الأخطاء ونقاط الضعف المحتملة في كود البرنامج. في هذه المقالة ، سنطلق PVS-Studio في السحابة بدلاً من محليًا على جهاز الكمبيوتر الخاص بالمطور لعدة أغراض وسوف نبحث عن أخطاء في PPSSPP.
وضع ترافيس سي
سنحتاج إلى مستودع على 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 ونضعه في جذر المشروع. كان لدى PPSSPP بالفعل ملف تكوين لـ Travis CI ، ومع ذلك ، كان كبيرًا جدًا وغير مناسب للمثال ، لذلك كان علينا تبسيطه وترك العناصر الأساسية فقط.
أولاً ، دعونا نحدد لغة البرمجة ، وإصدار 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 بإضافة الأوامر الخاصة بك لمراحل مختلفة من عمر الجهاز الافتراضي. يعمل المقطع قبل التثبيت قبل تثبيت الحزم. ثم
تثبيت ، والذي يتبع تثبيت الحزم من قائمة
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
قبل تثبيت الحزم ، دعونا نقوم بتحديث الوحدات الفرعية. هذا ضروري لبناء PPSSPPs. أضف الوظيفة الأولى إلى
.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://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 بتنزيل الأرشيف المحدد وتفريغه:
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 ".
إذا نجحت
البنية ، فسنصل إلى
after_success حيث سنجري تحليلًا ثابتًا:
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://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 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 } 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:
V597 يمكن للمترجم حذف استدعاء دالة "memset" ، والذي يتم استخدامه لمسح المخزن المؤقت "sum". يجب استخدام الدالة 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 Expression 'leftvol> = 0' صحيح دائمًا. sceAudio.cpp 120
إيلاء الاهتمام لفرع آخر لأول مرة
إذا . سيتم تنفيذ الكود فقط إذا كانت كل الشروط
leftvol> 0xFFFFF || rightvol> 0xFFFF || leftvol <0 || rightvol <0 خطأ. لذلك ، نحصل على العبارات التالية التي ستكون صحيحة للفرع الآخر:
leftvol <= 0xFFFFF ، rightvol <= 0xFFFFF ، leftvol> = 0 و rightvol> = 0 . إيلاء الاهتمام لآخر بيانين. هل من المعقول التحقق من الشرط الضروري لتنفيذ جزء الكود هذا؟
لذلك يمكننا حذف عوامل التشغيل الشرطية هذه بهدوء:
static u32 sceAudioOutputPannedBlocking (u32 chan, int leftvol, int rightvol, u32 samplePtr) { int result = 0;
سيناريو آخر. وراء هذه الظروف الزائدة هناك بعض الأخطاء. ربما فحصنا ما ليس ما نحتاجه ...
ضرب 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"); } .... }
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 وليس لتحليل المشروع بشكل كامل قدر الإمكان. إذا كنت تريد أخطاء أكبر وأكثر جمالا ، يمكنك دائما رؤيتها
هنا :).
استنتاج
يتيح لك استخدام خدمات الويب لإنشاء المشاريع جنبًا إلى جنب مع ممارسة التحليل التزايدي اكتشاف العديد من المشكلات فور دمج الكود. ومع ذلك ، قد لا يكون إنشاء واحد كافيًا ، لذا فإن إعداد الاختبار مع التحليل الثابت سيؤدي إلى تحسين جودة الكود بشكل كبير.
روابط مفيدة