>
>
>
Как быстро посмотреть интересные предуп…

Андрей Карпов
Статей: 673

Как быстро посмотреть интересные предупреждения, которые выдает анализатор PVS-Studio для C и C++ кода?

Время от времени программисты, которые начинают знакомиться с анализатором кода PVS-Studio, спрашивают: "Есть ли список предупреждений, которые точно указывают на ошибки?" Такого списка нет по той причине, что неинтересные (ложные) предупреждения в одном проекте, в другом оказываются очень важными и полезными. Однако начать знакомство с анализатором с самых интересных предупреждений вполне можно. Давайте рассмотрим эту тему подробнее.

Беда в том, что при первых запусках программист получает, как правило, огромное количество предупреждений, в которых "тонет". Естественно, у него возникает желание для начала познакомиться с самыми интересными предупреждениями, чтобы понять, стоит ли ему вообще тратить на всё это время. Отлично, вот три простых шага, которые позволят познакомиться с самыми интересными срабатываниями.

Шаг 1

Отключите все типы предупреждений кроме основных (GA). Распространённая ошибка: включить все виды предупреждений. Неопытным пользователям кажется, что чем больше всего включить, тем лучше. Это не так! Есть наборы диагностик, такие как 64-битные проверки и MISRA-правила, которые следует использовать, только чётко представляя, что это такое и как с ними работать. Например, включив MISRA-диагностики для обыкновенной прикладной программы, вы утонете в десятках, тысячах или сотнях тысяч сообщений типа:

  • V2506. MISRA. A function should have a single point of exit at the end.
  • V2507. MISRA. The body of a loop\conditional statement should be enclosed in braces.
  • V2523. MISRA. All integer constants of unsigned type should have 'u' or 'U' suffix.

Большинство MISRA-предупреждений указывают не на ошибки, а запахи кода. Естественно, человек начинает задаваться вопросами. Как в массе всех этих предупреждений найти что-то интересное? Диагностики под какими номерами он должен смотреть? Это неправильные вопросы. Нужно просто отключить набор MISRA. Это стандарт для написания качественного кода для встраиваемых устройств. Суть стандарта: сделать код крайне простым и понятным. Не надо пытаться применить его там, где он неуместен.

Примечание. Да, в стандарте MISRA есть правила, нацеленные на выявление настоящих ошибок. Пример: V2538 - The value of uninitialized variable should not be used. Но не бойтесь отключить MISRA-стандарт. Вы ничего не потеряете. Настоящие ошибки будут всё равно найдены в рамках диагностик общего назначения (GA). Например, неинициализированная переменная будет найдена благодаря диагностике V614.

Шаг 2

Любой статический анализатор при первых запусках выдаёт ложные срабатывания и требует определённой настройки. С этим ничего нельзя сделать, но это не так страшно, как может показаться. Даже простая быстрая настройка позволяет убрать большинство ложных предупреждений и начать знакомиться с уже вполне адекватным отчётом. Подробнее говорить про это не будем, так как я уже много раз писал про это, например, в этой статье: "Характеристики анализатора PVS-Studio на примере EFL Core Libraries, 10-15% ложных срабатываний".

Потратьте немного времени на отключение явно неактуальных предупреждений и борьбу с ложными срабатываниями из-за макросов. Макросы вообще являются основным источником ложных срабатываний, так как предупреждение появляется везде, где используется неудачно реализованный макрос. Для подавления предупреждений в макросах, рядом с их объявлением можно разместить комментарии специального вида. Подробнее формат написания комментариев рассматривается в документации.

Да, первоначальная настройка отнимет немного времени, но кардинально улучшит восприятие отчёта за счёт исключения отвлекающего шума. Уделите этому немного времени. Если есть какие-то затруднения или вопросы, мы всегда готовы помочь и подсказать как лучше настроить анализатор. Не стесняйтесь написать и задать нам вопросы.

Шаг 3

Начать изучать предупреждения с 1 уровня. И только затем смотреть 2 и 3. Уровни предупреждений являются ничем иным, как достоверностью предупреждения. Предупреждения 1-го уровня с большей вероятностью указывают на настоящую ошибку, чем 2.

Можно сказать, что выбирая "смотреть 1 уровень", вы нажимаете кнопку "смотреть самые интересные ошибки".

Более подробно классификация предупреждений PVS-Studio по уровням описана в статье "Как и почему статические анализаторы борются с ложными срабатываниями".

Так почему всё-таки нет списка?

Тем не менее, идея со списком наиболее полезных предупреждений может казаться все равно разумной. Давайте я на практическом примере покажу, что полезность диагностики относительна и зависит от проекта.

Рассмотрим предупреждение V550. Предупреждение выявляет потенциальную ошибку, связанную с тем, что для сравнения чисел с плавающей точкой используется оператор == или !=.

Большинство разработчиков, с которыми я общался, считают, что эта диагностика малополезна и отключают её, так как все срабатывания для их проекта являются ложными. И именно поэтому данная диагностика имеет низкую достоверность и располагается на 3-ем уровне.

Действительно, в большинстве приложений типы float/double используются в очень простых алгоритмах. И часто сравнение с константой используется исключительно для того, чтобы проверить, задано ли некое значение по умолчанию, или оно изменилось. В этом случае точная проверка вполне уместна. Поясню это псевдокодом.

float value = 1.0f;
if (IsUserInputNewValue())
  value = GetUserValue();
if (value == 1.0f)
  DefaultBehavior();
else
  Foo(value);

Здесь сравнение (value == 1.0f) корректно и безопасно.

Значит ли это, что V550 является неинтересной диагностикой? Нет. Всё зависит от проекта. Процитирую фрагмент из статьи "О том, как мы опробовали статический анализ на своем проекте учебного симулятора рентгенэндоваскулярной хирургии", написанной одним из наших пользователей.

Итак, на что здесь обращает наше внимание статический анализатор:

V550 An odd precise comparison: t != 0. It's probably better to use a comparison with defined precision: fabs(A - B) > Epsilon. objectextractpart.cpp 3401

D3DXVECTOR3 N = VectorMultiplication(
                  VectorMultiplication(V-VP, VN), VN);
float t = Qsqrt(Scalar(N, N));
if (t!=0)
{
  N/=t;
  V = V - N * DistPointToSurface(V, VP, N);
}

Подобные ошибки повторяются достаточно часто в данной библиотеке. Не скажу, что это стало для меня неожиданностью. Уже ранее наталкивался на некорректную работу с числами с плавающей точкой в этом проекте. Однако систематически проверять исходники на этот счет не было ресурсов. По результатам проверки стало ясно, что нужно дать разработчику почитать что-то для расширения кругозора в части работы с числами с плавающей точкой. Скинул ему ссылки на пару хороших статей. Посмотрим на результат. Сложно однозначно сказать, вызывает ли эта ошибка реальные сбои в работе программы. Текущее решение выставляет ряд требований к исходной полигональной сетке артерий, по которым моделируется растекание рентгеноконтрастного вещества. Если требования не выполнены, то возможны падения программы, или явно некорректная работа. Часть из этих требований получена аналитически, а часть - эмпирически. Не исключено, что эта вторая часть требований растет как раз из некорректной работы с числами с плавающей точкой. Нужно отметить, что не все найденные случаи употребления точного сравнения чисел с плавающей точкой являлись ошибкой.

Как видите, то что неинтересно в одних проектах, представляет интерес в других. Это делает невозможным создание списка "самого интересного".

Примечание. Имеется возможность задавать с помощью настроек уровень предупреждений. Например, если вы считаете, что диагностика V550 заслуживает пристального внимания, вы можете переместить её с третьего уровня на первый. Данный вид настроек описан в документации (см. "Как задать свой уровень для конкретной диагностики").

Заключение

Теперь вы знаете, как начать изучение предупреждений анализатора, рассматривая самые интересные из них. И не забывайте заглядывать в документацию, чтобы познакомиться с детальным описанием предупреждений. Иногда бывает, что за невзрачным, на первый взгляд, предупреждением кроется ад. Пример таких диагностик: V597, V1026. Спасибо за внимание.