استمرار: إهانة الآراء حول محللات الكود الثابت

image1.png


كان من المخطط بعد كتابة المقال "إنه عار على الآراء حول محللات الكود الثابت" ، سنتحدث بصراحة ونترك الموضوع بهدوء. لكن بشكل غير متوقع ، تسبب هذا المقال في استجابة عاصفة. للأسف ، سارت المناقشة بشكل خاطئ ، والآن سنقوم بمحاولة ثانية لشرح رؤيتنا للوضع.



الحكاية - القياس



لذا ، بدأ كل شيء بمقال " إنه لأمر مخز للآراء حول محللات الشفرات الثابتة ". لقد بدأ مناقشته بنشاط على بعض الموارد وهذه المناقشة تشبه إلى حد بعيد الحكاية القديمة التالية.
اشترينا منشارًا يابانيًا لبعض الحطاب السيبيري القاسي.

تجمع الحطابون في دائرة وقرروا اختبارها.

أحضروها وأعطوها شجرة.

قال المنشار الياباني: "زيبر".

"أوه ، اللعنة ..." - قال الحطاب.

ألقوا عليها شجرة أكثر سمكا. "Vzh-zh-zhik!" - قال المنشار.

"واو ، اللعنة!" - قال الحطاب.

وضعوا عليها أرزًا كثيفًا. "VZH-ZH-ZH-ZH-ZH-ZH-ZHIK !!!" - قال المنشار.

"واو ، اللعنة !!" - قال الحطاب.

ألقوا عليها قطعة من الحديد. "الكراك!" - قال المنشار.

"نعم ، اللعنة !!!" - قال حطاب سيبيريا شتيرن بتوبيخ! وتركوا ليقطعوا الغابة بالفؤوس ...
قصة واحد لواحد. نظر الناس إلى الكود:



if (A[0] == 0)
{
  X = Y;
  if (A[0] == 0)
    ....
}


وبدأوا في اختراع المواقف عندما يكون ذلك مبررًا ، مما يعني أن تحذير محلل PVS-Studio إيجابي كاذب. ذهب التفكير في الدورة التدريبية حول التغيير في الذاكرة بين فحصين ، ناشئين عن:



  • عمل التيارات المتوازية
  • معالجات الإشارة / المقاطعة ؛
  • المتغير X هو إشارة إلى العنصر A [0] ؛
  • الأجهزة ، مثل إجراء عمليات DMA ؛
  • إلخ


وبعد أن ناقشوا أنه ليست كل المواقف التي يمكن للمحلل فهمها ، فقد تركوا لتقطيع الغابة بالمحاور. أي أنهم وجدوا عذرًا لماذا من الممكن الاستمرار في عدم استخدام محلل كود ثابت في عملهم.



رؤيتنا للوضع



هذا النهج يأتي بنتائج عكسية. قد تكون الأداة غير الكاملة مفيدة ، لكن استخدامها مجدي اقتصاديًا.



نعم ، أي محلل ثابت يولد نتائج إيجابية خاطئة. ولا يمكن فعل شيء حيال ذلك. ومع ذلك ، فإن هذه المحنة مبالغ فيها إلى حد كبير. في الممارسة العملية ، يمكن تكوين أجهزة التحليل الثابتة واستخدامها بطرق مختلفة لقمع الإيجابيات الكاذبة والعمل معها (انظر 1 ، 2 ، 3 ، 4 ). بالإضافة إلى ذلك ، من المناسب هنا أن نتذكر مقال " الإيجابيات الكاذبة أعداؤنا ، لكن ربما يظلون أصدقاءك ".



ومع ذلك ، حتى هذا ليس هو الشيء الرئيسي. لا معنى للنظر في حالات خاصة من التعليمات البرمجية الغريبة!هل يمكنك الخلط بين المحلل ورمز معقد؟ نعم تستطيع. ومع ذلك ، في حالة واحدة من هذا القبيل ، سيكون هناك المئات من مشغلات المحلل المفيدة. يمكن العثور على العديد من الأخطاء وتصحيحها في مرحلة مبكرة جدًا. ويمكن قمع واحدة أو اثنتين من الإيجابيات الزائفة بأمان وعدم الالتفات إليها بعد الآن.



ومرة أخرى PVS-Studio على حق



هنا يمكن الانتهاء من المقال. ومع ذلك ، قد يعتبر البعض أن القسم السابق ليس اعتبارات منطقية ، ولكنه يحاول إخفاء نقاط الضعف والقصور في أداة PVS-Studio. لذلك عليك أن تستمر.



ضع في اعتبارك التعليمات البرمجية المجمعة الملموسة التي تتضمن تصريحات متغيرة:



void SetSynchronizeVar(int *);

int foo()
{
    int flag = 0;
    SetSynchronizeVar(&flag);

    int X, Y = 1;

    if (flag == 0)
    {
        X = Y;
        if (flag == 0)
            return 1;
    }
    return 2;
}


يقوم محلل PVS-Studio بإصدار تحذير معقول: V547 Expression 'flag == 0' صحيحة دائمًا.



والمحلل على حق تماما. إذا بدأ شخص ما في التشويش على أن متغيرًا يمكن أن يتغير في مؤشر ترابط آخر ، في معالج إشارة ، وما إلى ذلك ، فهو ببساطة لا يفهم لغات C و C ++. لا يمكنك الكتابة بهذه الطريقة.



لأغراض التحسين ، يحق للمترجم استبعاد الاختيار الثاني وسيكون محقًا تمامًا. من وجهة نظر اللغة ، لا يمكن للمتغير أن يتغير. تغيير خلفيتها ليس أكثر من سلوك غير محدد.



من أجل التحقق من أن تبقى في مكانها، يجب إعلان المتغير المتقلب :



void SetSynchronizeVar(volatile int *);

int foo()
{
    volatile int flag = 0;
    SetSynchronizeVar(&flag);
    ....
}


يعرف محلل PVS-Studio بهذا الأمر ولم يعد يصدر تحذيرًا لمثل هذا الرمز .



هنا نعود إلى ما تمت مناقشته في المقالة الأولى . لا توجد مشكلة. ولكن هناك انتقاد أو سوء فهم لماذا يحق للمحلل إصدار تحذير.



ملاحظة للقراء الأكثر دقة



قد يعود بعض القراء إلى المثال التركيبي من المقالة الأولى:



char get();
int foo(char *p, bool arg)
{
    if (p[1] == 1)
    {
        if (arg)
            p[0] = get();
        if (p[1] == 1)          // Warning
            return 1;
    }
    // ....
    return 3;
}


وإضافة متقلبة :



char get();
int foo(volatile char *p, bool arg)
{
    if (p[1] == 1)
    {
        if (arg)
            p[0] = get();
        if (p[1] == 1)          // Warning :-(
            return 1;
    }
    // ....
    return 3;
}


بعد ذلك ، من العدل أن نقول إن المحلل لا يزال يصدر التحذير V547 ، فإن التعبير 'p [1] == 1' صحيح دائمًا.



رائع ، لقد تبين أخيرًا أن المحلل لا يزال خاطئًا :). هذه نتيجة إيجابية خاطئة!



كما ترون ، نحن لا نخفي أي عيوب. عند تحليل تدفق البيانات لعناصر المصفوفة ، فقد هذا التقلب المشؤوم . تم العثور على الخلل وإصلاحه بالفعل. سيكون الإصلاح متاحًا في الإصدار التالي من المحلل. لن تكون هناك ايجابيات خاطئة.



لماذا لم يتم التعرف على هذا الخطأ في وقت سابق؟ لأنه في الواقع ، هذا مرة أخرى رمز غير حقيقي غير موجود في المشاريع الحقيقية. في الواقع ، لم نصادف مثل هذا الرمز حتى الآن ، على الرغم من أننا قمنا بفحص العديد من المشاريع مفتوحة المصدر .



لماذا الرمز غير واقعي؟ أولاً ، من الناحية العملية ، سيكون هناك نوع من وظيفة التوقيت أو التأخير بين الشيكين. ثانيًا ، لا أحد في عقله الصحيح ، ما لم يكن ذلك ضروريًا للغاية ، ينشئ مصفوفات تتكون من عناصر متقلبة. العمل مع مثل هذه المصفوفة هو انخفاض هائل في الأداء.



دعونا نلخص. من السهل إنشاء أمثلة يفشل فيها المحلل اللغوي. ولكن من الناحية العملية ، لا تؤثر العيوب المحددة عمليًا على جودة تحليل الكود وعدد الأخطاء الحقيقية المكتشفة. بعد كل شيء ، رمز التطبيقات الحقيقية هو مجرد كود يمكن فهمه في نفس الوقت للمحلل والشخص ، وليس إعادة توجيه أو لغز. إذا كان الرمز عبارة عن لغز ، فلا يوجد وقت للمحللين :).



شكرآ لك على أهتمامك.





روابط إضافية









إذا كنت ترغب في مشاركة هذه المقالة مع جمهور يتحدث الإنجليزية ، فيرجى استخدام رابط الترجمة: Andrey Karpov. الجزء 2: إزعاج الآراء حول أدوات التحليل الثابتة .



All Articles