للتحقق من جودة البرنامج ، يجب عليك استخدام العديد من الأدوات المختلفة ، بما في ذلك أجهزة التحليل الثابتة والديناميكية. في هذه المقالة ، سنحاول معرفة السبب في أن نوعًا واحدًا فقط من التحليل ، سواء أكان ثابتًا أم ديناميكيًا ، قد لا يكون كافيًا لتحليل البرنامج الشامل ولماذا يفضل استخدام الاثنين.
يكتب فريقنا الكثير عن فائدة التحليل الثابت والفوائد التي يحققها لمشاريعك. نود تشغيل الأداة الخاصة بنا في العديد من المشاريع مفتوحة المصدر للعثور على الأخطاء المحتملة ، والتي هي طريقتنا للترويج لطريقة تحليل الشفرة الثابتة. بدوره ، يساعد التحليل الثابت على جعل البرامج أكثر جودة وموثوقية وتقليل عدد نقاط الضعف المحتملة. ربما يكون لدى كل من يشارك مباشرة في العمل على شفرة المصدر هذا الشعور بالرضا عند إصلاح الخلل. ولكن حتى لو لم تنجح عملية اكتشاف الأخطاء (وإصلاحها) بنجاح في إنتاج الإندورفين ، فستستمتع بالتأكيد بفكرة تخفيض نفقات التطوير بفضل المحلل الثابت ، الذي ساعد المبرمجين على استخدام وقتهم بشكل أكثر فعالية وكفاءة. لمعرفة المزيد حول كيف يمكنك الاستفادة من استخدام التحليل الثابت من حيث المال ، راجع
هذه المقالة . يعطي تقديراً تقريبياً لـ PVS-Studio ، ولكن يمكن استقراء تلك النتائج إلى أدوات التحليل الثابتة الأخرى المتوفرة في السوق.
يبدو أن كل ما ذكر أعلاه يشير إلى أن الغرض من التحليل الثابت هو العثور على الأخطاء في التعليمات البرمجية المصدر في أقرب وقت ممكن ، وبالتالي تقليل النفقات على إصلاح الخلل. ولكن لماذا نحتاج إلى تحليل ديناميكي إذن ، ولماذا قد لا يكون الالتصاق بإحدى الطريقتين كافيًا؟ دعونا نقدم تعريفات أكثر رسمية وواضحة للتحليلات الثابتة والديناميكية ومحاولة الإجابة على هذه الأسئلة.
تحليل الكود الثابت هو عملية اكتشاف الأخطاء ورائحة الكود في الكود المصدري للبرنامج. لتحليل البرنامج ، لا تحتاج إلى تنفيذه ؛ سيتم إجراء التحليل على قاعدة الشفرة المتاحة. أقرب تشبيه للتحليل الثابت هو ما يسمى مراجعة الكود إلا أن التحليل الثابت هو نسخة آلية لمراجعة الكود (أي يتم تنفيذها بواسطة برنامج الروبوت).
أهم إيجابيات التحليل الثابت:
- اكتشاف الأخطاء في مراحل التطوير المبكرة. يساعد هذا في جعل إصلاح الأخطاء أرخص بكثير لأنه كلما تم اكتشاف عيب سابق ، أصبح الإصلاح أسهل ، وبالتالي أرخص.
- يسمح لك بتحديد موقع الأخطاء المحتملة بدقة في الكود المصدري.
- تغطية الرمز الكامل. بغض النظر عن عدد المرات التي تتحكم فيها كتلة واحدة من التعليمات البرمجية أو الأخرى أثناء التنفيذ ، يتحقق التحليل الثابت من قاعدة الشفرة بأكملها.
- سهل الاستخدام لا تحتاج إلى إعداد أي مجموعات بيانات الإدخال لإجراء الاختيار.
- يكتشف المحللون الثابتون الأخطاء المطبعية وأخطاء نسخ اللصق ذات الصلة بسرعة وسهولة.
سلبيات الهدف من التحليل الثابت:
- ايجابيات كاذبة لا مفر منها. يمكن للمحلل الثابت أن يغضب من شظايا الكود التي لا تحتوي على أي أخطاء فيها. يمكن للمبرمج فقط حل هذه المشكلة ووضع علامة تحذير كإيجابي خاطئ ، مما يعني أن الأمر سيستغرق بعض الوقت في العمل.
- التحليل الثابت سيئ بشكل عام في الكشف عن تسرب الذاكرة والأخطاء المرتبطة التزامن. للكشف عن مثل هذه الأخطاء ، يتعين عليك في الواقع تنفيذ جزء من البرنامج في الوضع الظاهري ، وهي مهمة صعبة للغاية. الى جانب ذلك ، فإن مثل هذه الخوارزميات تتطلب الكثير من الذاكرة ووقت وحدة المعالجة المركزية. عادةً لا تذهب المحولات الثابتة أبعد من تحليل بعض الحالات البسيطة. تعد أجهزة التحليل الديناميكية أكثر ملاءمة لتشخيص تسرب الذاكرة والأخطاء المرتبطة بالتزامن.
تجدر الإشارة إلى أن أجهزة التحليل الثابتة لا تركز بشكل حصري على اصطياد الأخطاء. على سبيل المثال ، يمكنهم تقديم توصيات بشأن تنسيق التعليمات البرمجية. تتيح لك بعض الأدوات التحقق من الكود الخاص بك للتأكد من توافقه مع معيار الترميز الذي تلتزم به شركتك. يتضمن ذلك المسافة البادئة للعديد من البنيات ، واستخدام أحرف المسافات / الجدولة ، وما إلى ذلك. بالإضافة إلى ذلك ، يمكن أن يكون التحليل الثابت مفيدًا لقياس المقاييس. يعد مقياس البرنامج مقياسًا كميًا لدرجة امتلاك البرنامج أو مواصفاته لبعض الممتلكات. راجع
هذه المقالة للتعرف على الاستخدامات الأخرى للتحليل الثابت.
تحليل الشفرة الديناميكية هو التحليل الذي يتم إجراؤه على البرنامج في وقت التنفيذ. هذا يعني أنه يجب عليك تحويل شفرة المصدر إلى ملف قابل للتنفيذ أولاً. بمعنى آخر ، لا يمكن التحقق من التعليمات البرمجية التي تحتوي على أخطاء تجميع أو بناء بواسطة هذا النوع من التحليل. يتم الفحص باستخدام مجموعة من بيانات المدخلات التي يتم تغذيتها للبرنامج قيد التحليل. لهذا السبب تعتمد فعالية التحليل الديناميكي بشكل مباشر على جودة وكمية بيانات إدخال الاختبار. هذه البيانات هي التي تحدد مدى تغطية الشفرة في نهاية الاختبار.
من خلال الاختبار الديناميكي ، يمكنك الحصول على المقاييس والتحذيرات التالية:
- الموارد المستخدمة: وقت تنفيذ البرنامج بأكمله أو أجزائه الفردية ، وعدد الاستعلامات الخارجية (على سبيل المثال ، إلى قاعدة بيانات) ، ومقدار ذاكرة الوصول العشوائي والموارد الأخرى التي يستخدمها البرنامج.
- مدى تغطية الشفرة عن طريق الاختبارات والمقاييس الأخرى.
- أخطاء البرامج: القسمة على صفر ، dereference فارغة ، تسرب الذاكرة ، ظروف السباق.
- بعض الثغرات الأمنية.
أهم إيجابيات التحليل الديناميكي:
- ليس لديك حق الوصول إلى الكود المصدري للبرنامج لتحليله. ومع ذلك ، تجدر الإشارة إلى أن أدوات التحليل الديناميكي مختلفة عن طريق تفاعلها مع البرنامج قيد التحليل (تتم مناقشة هذا بمزيد من التفاصيل هنا ). على سبيل المثال ، تشتمل إحدى أساليب التحليل الديناميكي الشائعة تمامًا على أدوات الكود قبل الفحص ، أي إضافة أجزاء من الشفرة الخاصة إلى الكود المصدري للتطبيق ليتمكن المحلل من تشخيص الأخطاء. في هذه الحالة ، يجب أن يكون لديك شفرة المصدر للبرنامج في متناول اليد.
- يمكنه اكتشاف أخطاء معالجة الذاكرة المعقدة مثل الفهرسة خارج حدود الصفيف وتسريبات الذاكرة.
- يمكنه تحليل التعليمات البرمجية متعددة مؤشرات الترابط في وقت التنفيذ ، وبالتالي اكتشاف المشاكل المحتملة التي لها علاقة بالوصول إلى الموارد المشتركة أو الجمود المحتمل.
- معظم تطبيقات المحللات الديناميكية لا تولد إيجابيات كاذبة حيث يتم اكتشاف الأخطاء فور حدوثها. لذلك ، فإن التحذير الصادر عن محلل ديناميكي ليس تنبؤًا تصدره الأداة استنادًا إلى تحليل نموذج البرنامج ، بل مجرد بيان لحقيقة حدوث خطأ.
سلبيات التحليل الديناميكي:
- تغطية الرمز الكامل غير مضمونة. وهذا يعني أنه من غير المرجح أن تحصل على تغطية بنسبة 100٪ عن طريق الاختبار الديناميكي.
- المحللون الديناميكيون سيئون في اكتشاف الأخطاء المنطقية. على سبيل المثال ، الشرط الحقيقي دائمًا ليس خللًا من منظور محلل ديناميكي نظرًا لأن مثل هذا الاختيار غير الصحيح يختفي ببساطة في وقت مبكر من خطوة التحويل البرمجي.
- من الصعب تحديد موقع الخطأ بدقة في الكود.
- من الصعب استخدام التحليل الديناميكي مقارنة بالتحليل الثابت حيث تحتاج إلى تغذية بيانات كافية للبرنامج للحصول على نتائج أفضل وتحقيق تغطية كاملة للرمز قدر الإمكان.
يعد التحليل الديناميكي مفيدًا بشكل خاص في تلك المجالات التي تكون فيها مصداقية البرنامج أو وقت الاستجابة أو الموارد المستهلكة هي الشاغل الرئيسي. بعض الأمثلة على مثل هذه الأنظمة ، نظام الوقت الحقيقي الذي يدير قطاع الإنتاج الحاسم أو خادم قاعدة البيانات. أي خطأ في هذه المناطق يمكن أن يكون حاسما.
نعود إلى السؤال عن سبب عدم كفاية الالتزام بأحد هذين النوعين من التحليل ، فلنلقِ نظرة على أمثلة تافهة تمامًا عن الأخطاء التي لا تواجه إحدى طرق التحليل مشاكل في تشخيصها ، بينما لا يمكن للطريقة الأخرى اكتشافها والعكس صحيح.
المثال التالي مأخوذ من مشروع كلانج:
MapTy PerPtrTopDown; MapTy PerPtrBottomUp; void clearBottomUpPointers() { PerPtrTopDown.clear(); } void clearTopDownPointers() { PerPtrTopDown.clear(); }
قد يشير محلل ثابت إلى أن أجسام الوظيفتين متطابقتان. بطبيعة الحال ، فإن وظيفتين لهما أجسام متطابقة ليست بالضرورة علامة محددة على وجود خطأ ، ولكن من المحتمل جدًا أنها نتجت عن استخدام تقنية نسخ اللصق جنبًا إلى جنب مع الإهمال من جانب المبرمج - وهذا يؤدي إلى سلوك غير متوقع. في هذه الحالة ، يجب على الأسلوب
clearBottomUpPointers استدعاء الأسلوب
PerPtrBottomUp.clear . لن يلاحظ التحليل الديناميكي أي شيء خاطئ في هذا المثال لأنه رمز شرعي تمامًا من وجهة نظره.
مثال آخر لنفترض أن لدينا الوظيفة التالية:
void OutstandingIssue(const char *strCount) { unsigned nCount; sscanf_s(strCount, "%u", &nCount); int array[10]; memset(array, 0, nCount * sizeof(int)); }
من الناحية النظرية ، يمكن للمحلل الثابت أن يشك في وجود شيء خاطئ في هذا الرمز ، لكن تنفيذ مثل هذا التشخيص يعد مهمة صعبة للغاية ولا معنى لها. المثال مأخوذ من
هذه المقالة ، والذي يوضح أيضًا لماذا من المستحسن تعليم المحللين الاستاتيكيين كيفية تشخيص أخطاء مثل هذه. باختصار ، يعتبر المحللون
الثابتون سيئين للغاية في معرفة أن استدعاء دالة
memset قد يؤدي إلى فهرسة تتجاوز حدود المصفوفة حيث لا يمكنهم التنبؤ بالرقم الذي سيتم قراءته من سلسلة
strCount ؛ وإذا
تمت قراءة قيمة
strCount من ملف ، فإنها تصبح مهمة مستحيلة للتحليل الثابت تمامًا. من ناحية أخرى ، لن يواجه أي محلل ديناميكي أي مشكلة في الإشارة إلى خطأ معالجة الذاكرة في هذا الرمز (مع العلم أن البرنامج يتم تغذية البيانات الصحيحة).
لا تهدف هذه المقالة إلى مقارنة التحليلات الثابتة والديناميكية. لا توجد تقنية واحدة يمكنها تشخيص مجموعة كاملة من عيوب البرمجيات. لا يمكن لأي نوع من التحليل أن يحل محل الآخر تمامًا. لتحسين جودة برامجك ، يجب عليك استخدام أنواع مختلفة من الأدوات بحيث تكمل بعضها البعض. آمل أن تكون الأمثلة الموضحة أعلاه مقنعة بما فيه الكفاية.
لا أرغب في أن أبدو متحيزًا للغاية تجاه التحليل الثابت ، ولكن هذه التقنية هي الأكثر شيوعًا ، والأهم من ذلك ، تضمينها من قبل الشركات في عمليات CI الخاصة بها مؤخرًا. يعمل التحليل الثابت كواحدة من خطوات ما يسمى بوابات الجودة لبناء منتج برمجي موثوق وعالي الجودة. نعتقد أن التحليل الثابت سيصبح ممارسة قياسية لتطوير البرمجيات في بضع سنوات ، تمامًا مثلما حدث مع اختبار الوحدات.
في الختام ، أود أن أشير مرة أخرى إلى أن التحليل الديناميكي والتحليل الثابت هما طريقتان مختلفتان ، يكمل كل منهما الآخر. في النهاية ، تخدم كل هذه التقنيات غرضًا واحدًا هو زيادة جودة البرامج وتقليل نفقات التطوير.
المراجع:- المصطلحات. تحليل كود ثابت .
- المصطلحات. تحليل الشفرة الديناميكية .
- أندريه كاربوف. تحليل الشفرة الثابتة والديناميكية .
- أندريه كاربوف. الخرافات حول التحليل الثابت. الأسطورة الثالثة - التحليل الديناميكي أفضل من التحليل الثابت .
- أندريه كاربوف. PVS-Studio ROI .