نواصل سلسلة من المقالات حول استخدام محلل ثابت PVS-Studio في أنظمة سحابة CI. اليوم نحن نفكر في خدمة أخرى - CircleCI. هذه المرة ، سيكون مشغل الوسائط Kodi بمثابة مشروع للتحليل ، في الكود المصدري الذي سنحاول إيجاد أماكن مثيرة للاهتمام فيه.
المذكرة. يمكن الاطلاع على مقالات أخرى حول دمج PVS-Studio في أنظمة CI السحابية هنا:
قبل أن نبدأ مباشرة في إعداد وتحليل تحذيرات المحللين ، دعنا نقول بضع كلمات عن البرنامج المستخدم والمحلل.
CircleCI هي خدمة CI قائمة على السحابة لأتمتة تجميع البرامج واختبارها ونشرها. وهو يدعم تجميع المشاريع في كل من الحاويات والأجهزة الظاهرية التي تعمل بنظام Windows و Linux و macOS.
Kodi هي مشغل وسائط مجاني ومفتوح المصدر. يتيح لك تشغيل ملفات الصوت والفيديو الموجودة على الكمبيوتر المنزلي وعلى شبكة محلية أو الإنترنت. وهو يدعم السمات والوظائف عن طريق تثبيت الإضافات. متاح لنظام التشغيل Windows و Linux و macOS و Android.
PVS-Studio هو محلل ثابت للكود للبحث عن الأخطاء ونقاط الضعف المحتملة في الكود المكتوب بلغات C و C ++ و C # و Java. يعمل تحت ويندوز ، لينكس وماك.
تعديل
انتقل إلى الصفحة الرئيسية لـ
CircleCI وانقر فوق الزر "تسجيل"
في الصفحة التالية ، سيُطلب منا المصادقة باستخدام حساب GitHub أو Bitbucket. حدد GitHub وانتقل إلى صفحة تفويض تطبيق CircleCI.
نحن نسمح بالتطبيق (الزر الأخضر "Authorise circleci") وإعادة توجيهنا إلى صفحة الترحيب "Welcome to CircleCI!"
في هذه الصفحة ، يمكننا تهيئة المشروعات التي سيتم تجميعها في CircleCI على الفور. نحتفل بمستودعنا وانقر فوق "متابعة".
بعد إضافة المخزون ، سيبدأ CircleCI تلقائيًا في الإنشاء ، لكن منذ ذلك الحين لا يوجد ملف تكوين في المستودع حتى الآن - ستفشل مهمة الإنشاء.
قبل إضافة ملف التكوين ، سنضيف إلى متغيرات المشروع التي تحتوي على بيانات الترخيص للمحلل. للقيام بذلك ، انقر فوق "الإعدادات" في الجزء الأيمن ، ثم في مجموعة "التنظيم" حدد عنصر "المشاريع" وانقر على الترس الموجود على يمين المشروع الذي نحتاج إليه. سيتم فتح نافذة الإعدادات.
نحن مهتمون بقسم "متغيرات البيئة". نذهب إليه
وننشئ متغيرات
PVS_USERNAME و
PVS_KEY التي تحتوي على اسم المستخدم ومفتاح الترخيص للمحلل.
عند بدء إنشاء مشروع ، يقرأ CircleCI تكوين المهمة من ملف في المستودع على طول المسار .circleci / config.yml. أضفه
أولاً ، نشير إلى صورة الجهاز الظاهري حيث سيبدأ المحلل. قائمة كاملة من الصور متاحة
هنا .
version: 2 jobs: build: machine: image: ubuntu-1604:201903-01
بعد ذلك ، أضف المستودعات اللازمة لتركيب وتثبيت تبعيات المشروع:
steps: - checkout - run: sudo -- sh -c " add-apt-repository -y ppa:team-xbmc/xbmc-ppa-build-depends && add-apt-repository -y ppa:wsnipex/vaapi && add-apt-repository -y ppa:pulse-eight/libcec && apt-get update" - run: sudo apt-get install -y automake autopoint build-essential cmake curl default-jre gawk gdb gdc gettext git-core gperf libasound2-dev libass-dev libbluray-dev libbz2-dev libcap-dev libcdio-dev libcec4-dev libcrossguid-dev libcurl3 libcurl4-openssl-dev libdbus-1-dev libegl1-mesa-dev libfmt3-dev libfontconfig-dev libfreetype6-dev libfribidi-dev libfstrcmp-dev libgif-dev libgl1-mesa-dev libglu1-mesa-dev libiso9660-dev libjpeg-dev liblcms2-dev libltdl-dev liblzo2-dev libmicrohttpd-dev libmysqlclient-dev libnfs-dev libpcre3-dev libplist-dev libpng-dev libpulse-dev libsmbclient-dev libsqlite3-dev libssl-dev libtag1-dev libtinyxml-dev libtool libudev-dev libusb-dev libva-dev libvdpau-dev libxml2-dev libxmu-dev libxrandr-dev libxrender-dev libxslt1-dev libxt-dev mesa-utils nasm pmount python-dev python-imaging python-sqlite rapidjson-dev swig unzip uuid-dev yasm zip zlib1g-dev wget
أضف مستودع PVS-Studio وقم بتثبيت المحلل:
- run: wget -q -O - https:
دعنا نجمع تبعيات المشروع:
- run: sudo make -C tools/depends/target/flatbuffers PREFIX=/usr/local
إنشاء Makefiles في دليل التجميع:
- run: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug ..
والخطوة التالية هي تكوين وتشغيل تحليل ثابت لمشروعنا.
أولاً ، قم بإنشاء ملف برخصة محلل. يبدأ الأمر الثاني في ترجمة تتبع تجميع المشروع.
بعد التتبع ، نبدأ مباشرة التحليل الثابت. عند استخدام ترخيص تجريبي ، يجب أن يبدأ المحلل بالمعلمة:
--DisableLicenseExpirationCheck .
يقوم الأمر الأخير بتحويل الملف بنتائج المحلل إلى تقرير html:
- run: pvs-studio-analyzer credentials -o PVS.lic ${PVS_USER} ${PVS_KEY} - run: pvs-studio-analyzer trace -- make -j2 -C build/ - run: pvs-studio-analyzer analyze -j2 -l PVS.lic -o PVS-Studio.log --disableLicenseExpirationCheck - run: plog-converter -t html -o PVS-Studio.html PVS-Studio.log
بعد الانتهاء من الاختبارات ، احفظ تقارير المحلل:
- run: mkdir PVS_Result && cp PVS-Studio.* ./PVS_Result/ - store_artifacts: path: ./PVS_Result
النص الكامل لملف .circleci / config.yml:
version: 2.1 jobs: build: machine: image: ubuntu-1604:201903-01 steps: - checkout - run: sudo -- sh -c " add-apt-repository -y ppa:team-xbmc/xbmc-ppa-build-depends && add-apt-repository -y ppa:wsnipex/vaapi && add-apt-repository -y ppa:pulse-eight/libcec && apt-get update" - run: sudo apt-get install -y automake autopoint build-essential cmake curl default-jre gawk gdb gdc gettext git-core gperf libasound2-dev libass-dev libbluray-dev libbz2-dev libcap-dev libcdio-dev libcec4-dev libcrossguid-dev libcurl3 libcurl4-openssl-dev libdbus-1-dev libegl1-mesa-dev libfmt3-dev libfontconfig-dev libfreetype6-dev libfribidi-dev libfstrcmp-dev libgif-dev libgl1-mesa-dev libglu1-mesa-dev libiso9660-dev libjpeg-dev liblcms2-dev libltdl-dev liblzo2-dev libmicrohttpd-dev libmysqlclient-dev libnfs-dev libpcre3-dev libplist-dev libpng-dev libpulse-dev libsmbclient-dev libsqlite3-dev libssl-dev libtag1-dev libtinyxml-dev libtool libudev-dev libusb-dev libva-dev libvdpau-dev libxml2-dev libxmu-dev libxrandr-dev libxrender-dev libxslt1-dev libxt-dev mesa-utils nasm pmount python-dev python-imaging python-sqlite rapidjson-dev swig unzip uuid-dev yasm zip zlib1g-dev wget - run: wget -q -O - https:
نقوم بتحميل الملف إلى المستودع وسوف تبدأ CircleCI تلقائيًا في تجميع المشروع.
بعد الانتهاء من المهمة ، يمكن تنزيل الملفات التي تحتوي على نتائج محلل في علامة التبويب "القطع الأثرية".
نتائج التحليل
حسنًا ، دعونا الآن نلقي نظرة على بعض التحذيرات التي أصدرها المحلل أثناء العمل.
تحذير PVS-Studio :
V504 من المحتمل جدًا أن تكون الفاصلة المنقوطة '؛' مفقود بعد الكلمة الرئيسية "عودة". AdvancedSettings.cpp: 1476
void CAdvancedSettings::SetExtraArtwork(const TiXmlElement* arttypes, std::vector<std::string>& artworkMap) { if (!arttypes) return artworkMap.clear(); const TiXmlNode* arttype = arttypes->FirstChild("arttype"); .... }
اذا حكمنا من خلال تنسيق الكود ، تم افتراض منطق التنفيذ التالي:
- إذا كانت arttypes عبارة عن مؤشر فارغ ، أكمل تنفيذ الطريقة ؛
- إذا كانت arttypes عبارة عن مؤشر غير صفري ، فقم بمسح متجه artworkMap ، ثم قم بتنفيذ بعض الإجراءات الأخرى.
ومع ذلك ، فإن الشخصية المفقودة "؛" إجراء تعديلات ؛ نتيجة لذلك ، لا يتوافق منطق التنفيذ مع التنسيق على الإطلاق. نتيجة لذلك ، يصبح ما يلي:
- إذا كانت arttypes عبارة عن مؤشر فارغ ، فسيتم مسح متجه artworkMap وتخرج الطريقة ؛
- إذا كانت arttypes عبارة عن مؤشر غير صفري ، فسيتم تنفيذ إجراءات أخرى ، لكن لم يتم تنظيف متجه artworkMap .
بشكل عام ، من غير المرجح أن يكون هناك خطأ. ولا يكاد أي شخص يكتب التعبيرات بروح
العودة artworkMap.clear () ؛ :).
تحذيرات PVS-Studio :
- V547 التعبير "lastsector" هو دائما خطأ. udf25.cpp: 636
- V547 التعبير "lastsector" هو دائما خطأ. udf25.cpp: 644
- الاختيار المتكرر V571 . تم التحقق بالفعل من الشرط "if (lastsector)" في السطر 636. udf25.cpp: 644
int udf25::UDFGetAVDP( struct avdp_t *avdp) { .... uint32_t lastsector; .... lastsector = 0;
انتبه إلى الأماكن التي تحمل علامة
// <= . تتم كتابة القيمة 0 إلى متغير
lastsector ، ثم يتم استخدامها مرتين كتعبير شرطي عن عبارة
if . نظرًا لأن قيمة المتغير لا تتغير سواء في الحلقة أو بين هذه التعيينات ، فإن فروع الاثنين
إذا لم يتم تنفيذ
العبارات .
قد يكون صحيحًا أن الوظيفة الضرورية لم يتم تنفيذها بعد (يجب الانتباه إلى ما يجب عمله).
بالمناسبة ، كما ترون ، أصدر المحلل على الفور 3 تحذيرات لهذا الرمز. ومع ذلك ، في بعض الأحيان ، لا تكفي بعض التحذيرات ، ولا يزال المستخدمون يعتقدون أن المحلل مخطئ ... كتب أحد الزملاء المزيد حول هذا الموضوع في المقالة "
يوم واحد من دعم مستخدم PVS-Studio " :).
تحذير PVS-Studio : تعبير
V547 'values.size ()! = 2' خطأ دائمًا. GUIControlSettings.cpp: 1174
bool CGUIControlRangeSetting::OnClick() { .... std::vector<CVariant> values; SettingConstPtr listDefintion = settingList->GetDefinition(); switch (listDefintion->GetType()) { case SettingType::Integer: values.push_back(m_pSlider-> GetIntValue(CGUISliderControl::RangeSelectorLower)); values.push_back(m_pSlider-> GetIntValue(CGUISliderControl::RangeSelectorUpper)); break; case SettingType::Number: values.push_back(m_pSlider-> GetFloatValue(CGUISliderControl::RangeSelectorLower)); values.push_back(m_pSlider-> GetFloatValue(CGUISliderControl::RangeSelectorUpper)); break; default: return false; } if (values.size() != 2) return false; SetValid(CSettingUtils::SetList(settingList, values)); return IsValid(); }
في هذه الحالة ، يكون التحقق من
value.size ()! = 2 ضروريًا ، لأن تعبير الشرطية سيكون دائمًا
خطأ . في الواقع ، إذا تم تنفيذ التنفيذ في أحد فروع
الحالة الخاصة
ببيان التبديل ، فستتم إضافة عنصرين إلى المتجه ، وبما أنه فارغ ، فسيصبح حجمه مساويا لاثنين ؛ وإلا (عند تنفيذ الفرع
الافتراضي ) ، سيتم إنهاء الطريقة.
تحذير PVS-Studio :
V547 Expression 'prio == 0x7fffffff' صحيح دائمًا. DBusReserve.cpp: 57
bool CDBusReserve::AcquireDevice(const std::string& device) { .... int prio = INT_MAX; .... res = dbus_bus_request_name( m_conn, service.c_str(), DBUS_NAME_FLAG_DO_NOT_QUEUE | (prio == INT_MAX ? 0 : DBUS_NAME_FLAG_ALLOW_REPLACEMENT),
تتم تهيئة متغير
prio بالقيمة
INT_MAX ، وبعد ذلك يتم استخدامه أيضًا في العامل الثلاثي في المقارنة
prio == INT_MAX . ومع ذلك ، بين مكان التهيئة والاستخدام ، لا تتغير قيمته ، وبالتالي ، فإن قيمة التعبير
prio == INT_MAX صحيحة ، وسيُرجع المشغل الثلاثي دائمًا 0.
تحذيرات PVS-Studio :
- V575 يتم تمرير المؤشر الفارغ المحتمل في وظيفة "memcpy". تفقد الحجة الأولى. خطوط التحقق: 39 ، 38. DVDOverlayImage.h: 39
- V575 يتم تمرير المؤشر الفارغ المحتمل في وظيفة "memcpy". تفقد الحجة الأولى. خطوط التحقق: 44 ، 43. DVDOverlayImage.h: 44
CDVDOverlayImage(const CDVDOverlayImage& src) : CDVDOverlay(src) { Data = (uint8_t*)malloc(src.linesize * src.height); memcpy(data, src.data, src.linesize * src.height);
كلا التحذيرين لهما نفس النمط - يتم استخدام المؤشر الذي تم الحصول عليه نتيجة استدعاء وظيفة
malloc في وظيفة
memcpy دون التحقق أولاً من
NULL .
قد يرغب شخص ما في الاعتراض على هذه التحذيرات في المفتاح التالي: لن يقوم
malloc بإرجاع مؤشر فارغ أبدًا ، وإذا حدث ذلك ، فدع التطبيق يسقط بشكل أفضل. هذا موضوع للمناقشة الطويلة ، لكنني اقترح بطريقة أو بأخرى قراءة ملاحظة زميلي - "
لماذا من المهم التحقق من إرجاع وظيفة malloc ".
إذا كنت ترغب في ذلك ، يمكنك تكوين المحلل ليتصرف بطريقة لا تعتبر أن
malloc يمكنه إرجاع مؤشر فارغ - فلن يكون هناك مثل هذه التحذيرات. اقرأ المزيد عن هذا
هنا .
تحذير PVS-Studio :
V522 قد يكون هناك
إلغاء لإشارة "إدخال" مؤشر فارغ. خطوط التحقق: 985 ، 981. emu_msvcrt.cpp: 985
struct dirent *dll_readdir(DIR *dirp) { .... struct dirent *entry = NULL; entry = (dirent*) malloc(sizeof(*entry)); if (dirData->curr_index < dirData->items.Size() + 2) { if (dirData->curr_index == 0) strncpy(entry->d_name, ".\0", 2); .... }
الموقف مشابه للحالة الموضحة أعلاه. تتم كتابة المؤشر الذي تم الحصول عليه نتيجة لاستدعاء
malloc إلى متغير
الإدخال ، وبعد ذلك يتم استخدامه دون التحقق من
NULL (
إدخال-> d_name ).
تحذير PVS-Studio : تم
إنهاء نطاق الرؤية
V773 لمؤشر 'progressHandler' دون تحرير الذاكرة. تسرب الذاكرة ممكن. PVRGUIChannelIconUpdater.cpp: 94
void CPVRGUIChannelIconUpdater::SearchAndUpdateMissingChannelIcons() const { .... CPVRGUIProgressHandler* progressHandler = new CPVRGUIProgressHandler(g_localizeStrings.Get(19286)); for (const auto& group : m_groups) { const std::vector<PVRChannelGroupMember> members = group->GetMembers(); int channelIndex = 0; for (const auto& member : members) { progressHandler->UpdateProgress(member.channel->ChannelName(), channelIndex++, members.size()); .... } progressHandler->DestroyProgress(); }
يحتوي مؤشر
progressHandler على القيمة التي تم الحصول عليها عن طريق استدعاء
عامل التشغيل الجديد . ومع ذلك ، لا
يتم استدعاء
حذف عامل التشغيل لهذا المؤشر ، والذي يتسبب في حدوث تسرب للذاكرة.
تحذير PVS-Studio : تجاوز
سعة الصفيف
V557 أمر ممكن. يشير مؤشر 'idx' إلى ما وراء مجموعة الصفيف. PlayerCoreFactory.cpp: 240
std::vector<CPlayerCoreConfig *> m_vecPlayerConfigs; bool CPlayerCoreFactory::PlaysVideo(const std::string& player) const { CSingleLock lock(m_section); size_t idx = GetPlayerIndex(player); if (m_vecPlayerConfigs.empty() || idx > m_vecPlayerConfigs.size()) return false; return m_vecPlayerConfigs[idx]->m_bPlaysVideo; }
يفرض
عبارة if قيودًا على حجم المتجه
m_vecPlayerConfigs بسبب التعبير الشرطي والخروج من الطريقة إذا كانت صحيحة. نتيجة لذلك ، إذا وصل تنفيذ التعليمات البرمجية إلى
عبارة الإرجاع الأخيرة ، يكون حجم المتجه
m_vecPlayerConfigs في النطاق المحدد: [1؛ IDX]. ومع ذلك ، يوجد سطرين أدناه هو
استدعاء idx :
m_vecPlayerConfigs [idx] -> m_bPlaysVideo . نتيجة لذلك ، إذا كانت
idx تساوي حجم المتجه ، فسيتجاوز التطبيق النطاق المقبول.
وأخيرًا ، ألقِ نظرة على اثنين من التحذيرات على رمز مكتبة
Platinum .
تحذير PVS-Studio :
V542 خذ بعين الاعتبار فحص نوع فردي: "منطقي" إلى "char *". PltCtrlPoint.cpp: 1617
NPT_Result PLT_CtrlPoint::ProcessSubscribeResponse(...) { .... bool subscription = (request.GetMethod().ToUppercase() == "SUBSCRIBE"); .... NPT_String prefix = NPT_String::Format(" PLT_CtrlPoint::ProcessSubscribeResponse %ubscribe for service \"%s\" (result = %d, status code = %d)", (const char*)subscription?"S":"Uns",
في هذه الحالة ، تكون أولوية العمليات مشوشة.
Const char * ليس نتيجة حساب المشغل الثلاثي (
الاشتراك؟ "S": "Uns" ) ، ولكن
الاشتراك المتغير. على الأقل يبدو غريبا.
تحذير PVS-Studio :
V560 جزء من التعبير الشرطي خطأ دائمًا: c == '\ t'. NptUtils.cpp: 863
NPT_Result NPT_ParseMimeParameters(....) { .... case NPT_MIME_PARAMETER_PARSER_STATE_NEED_EQUALS: if (c < ' ') return NPT_ERROR_INVALID_SYNTAX;
رمز الفضاء هو 0x20 ، رمز علامة التبويب هو 0x09. لذلك ، سيكون التعبير الفرعي
c == '\ t' خاطئًا دائمًا ، لأن هذه الحالة مغطى بالفعل بالتعبير
c <'' (إذا كان هذا صحيحًا ، فسيتم إنهاء الوظيفة).
استنتاج
كما ترون من هذه المقالة ، في نظام CI التالي (CircleCI) ، تمكنا من تكوين التحقق من المشروع باستخدام PVS-Studio. أقترح عليك
تنزيل وتجربة المحلل في مشروعك. إذا كان لديك أي أسئلة حول تكوين أو استخدام المحلل ، فلا تتردد في
الكتابة إلينا ، وسنكون سعداء للمساعدة.
وبالطبع ، رمز الإهمال بالنسبة لك ، أيها الأصدقاء. :)

إذا كنت ترغب في مشاركة هذا المقال مع جمهور ناطق باللغة الإنجليزية ، فيرجى استخدام الرابط الخاص بترجمة: Sergey Vasiliev، Ilya Gainulin.
PVS-Studio in the Clouds: CircleCI .