Наша команда анализирует множество проектов с открытым исходным кодом, чтобы продемонстрировать диагностические возможности анализатора PVS-Studio. Прочитав наши статьи, люди часто будут спрашивать: «Как вообще программа работает со всеми этими ошибками?» В этой статье я постараюсь ответить на этот вопрос.

Введение

Для начала позвольте мне сказать несколько слов тем нашим читателям, которые еще не знакомы с нашим инструментом. Мы разрабатываем анализатор PVS-Studio, предназначенный для поиска ошибок в исходном коде C/C++. Лучший способ продемонстрировать его возможности — проанализировать проекты с открытым исходным кодом, чтобы найти все возможные ошибки. Все найденные выпуски собираются в специальной базе данных. Когда мы находим ошибки, которые нам кажутся интересными, мы обсуждаем их в статьях. Для получения дополнительных статей проверьте наш актуальный список статей.

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

Самая важная вещь

Разные фрагменты кода выполняются с разной частотой. Одни выполняются при каждом прогоне, другие время от времени, а есть и фрагменты, исполняемые в крайне редких случаях.

Чем реже выполняется определенный фрагмент кода, тем легче в нем спрятаться багу. Попробую объяснить на ряде примеров.

Если ошибка приводит к сбою при рисовании большой кнопки в главном окне программы, она сразу же будет обнаружена и, вероятно, исправлена ​​самим программистом до того, как она попадет в отдел тестирования.

Если ошибка зависит от входных данных, ее немного легче скрыть. Предположим, программист разрабатывает графический редактор. Он или она протестировали программу на изображениях с разрешением 100x100 и 300x400, и хотя код написан плохо, все равно работает без сбоев. К счастью, у компании есть тестировщики, которые обнаружили, что программа не может работать с растянутыми изображениями с разрешением 100х10000. Как видите, эта ошибка сохранилась немного дольше.

Еще труднее найти ошибки, для возникновения которых требуются особые условия. Возвращаясь к нашему графическому редактору, предположим, что он отлично справляется с изображениями среднего и большого размера, но не может работать с изображениями 1x1, которые нужно обрабатывать особым образом, а проверить это ни программист, ни тестировщики не додумались. этот режим. Это приводит к сбою программы, когда какой-либо пользователь случайно создает изображение размером в 1 пиксель. На этот раз ошибка дошла до конечного пользователя.

Но есть ошибки, которые еще труднее обнаружить. Вы можете найти их в ветках кода, отвечающих за обработку различных нестандартных ситуаций — например, сбой при сохранении файла. Такой баг может жить в программе годами, никому неизвестен, и проявляться только тогда, когда пользователь теряет какие-то важные данные из-за того, что на жестком диске закончилось место, и программа, вместо того, чтобы предупредить об этом и предложить сохранить файл в другое место, как предполагалось, просто вылетает из-за этой ошибки.

Отвечая на вопрос

Большинство ошибок, которые мы обнаруживаем при анализе open-source проектов с помощью PVS-Studio, не влияют на повседневную нормальную работу программы для тысяч пользователей. Все эти ошибки находятся в фрагментах кода, которые выполняются очень редко, поэтому с ними никому не приходится иметь дело.

И по-другому быть не может. Возьмем, к примеру, библиотеку Qt. Эта библиотека полностью отлажена и протестирована и используется в своей работе огромным количеством разработчиков. Он просто не может переносить лежащих на поверхности жуков. Поэтому PVS-Studio находит только те баги, которые редко себя проявляют. В качестве примера рассмотрим следующую функцию:

QV4::ReturnedValue
QQuickJSContext2DPrototype::method_getImageData(....)
{
  ....
  qreal x = ctx->callData->args[0].toNumber();
  qreal y = ctx->callData->args[1].toNumber();
  qreal w = ctx->callData->args[2].toNumber();
  qreal h = ctx->callData->args[3].toNumber();
  if (!qIsFinite(x) || !qIsFinite(y) ||
      !qIsFinite(w) || !qIsFinite(w))
  ....
}

Содержит ошибку — не проверяется переменная h. Вместо этого дважды проверяется переменная «w». Это ошибка? Да, это. Но вряд ли появится в ближайшее время.

Во-первых, не все приложения, использующие библиотеку Qt, вызывают функцию method_getImageData(). Я считаю, что на самом деле вряд ли кто-то использует его. Во-вторых, ошибка появится только в том случае, если последний аргумент будет неправильно преобразован (с остальными аргументами проблем нет). В-третьих, этот последний аргумент должен быть неправильно определен. Таким образом, эта ошибка вряд ли когда-либо появится.

Именно поэтому он долгое время сохранялся в коде библиотеки Qt, пока не был обнаружен PVS-Studio. Кстати, если вы хотите подробнее узнать об этой проверке, вот статья Проверка Qt 5 Framework.

Теперь подведем итоги.

При разработке и сопровождении программ разработчики используют ряд методов их тестирования (юнит-тесты, регрессионные тесты, ручные тесты и т. д.). Явные ошибки быстро устраняются, так как их быстро обнаруживают как сами разработчики, так и пользователи продукта.

Поэтому, когда мы запускаем анализатор PVS-Studio на каком-нибудь известном и надежном проекте, таком как Chromium, мы можем только надеяться найти баги, которые почти никогда себя не обнаруживают.

Я имею в виду, что баги остались — их количество (см. проверки N1, N2, N3, N4). Но вы вряд ли столкнетесь с ними при работе в Chromium. Потребуется усилие, возможно очень большое, чтобы добраться до ветки кода, где живет ошибка.

Тогда не нужно использовать PVS-Studio?

Дочитав до этого места, можно поспешить сделать неверный вывод: «Не нужно использовать PVS-Studio, так как он выявляет незначительные баги, которые практически никогда не проявляются».

Обычно, когда я чувствую, что кто-то вот-вот сделает такой вывод, я отсылаю его к статье «Лев Толстой и статический анализ кода». Но в этот раз я постараюсь еще раз выразить ответ другими словами.

Видите ли, наши статьи и разовые проверки open-source проектов имеют очень мало общего с обычным использованием методологии статического анализа кода. Разовые чеки хороши для рекламы продукта, но не более того. От них действительно мало толку. Вы можете извлечь выгоду из статического анализа только при регулярном его использовании.

Худшее, что вы можете сделать, — это прогнать свой код через анализатор незадолго до релиза. Это просто бессмысленно. Количество и количество ошибок, которые анализатор мог уловить для вас, уже найдено и исправлено к тому времени ценой пота и крови. Это неэффективный способ использования инструмента (вот, например, пост о том, как можно потратить 50 часов рабочего времени). После нескольких бессонных ночей, проведенных в отладчике и переписке с тестировщиками, разработчики запускают анализатор только для того, чтобы получить пару полезных сообщений. Что толку, когда они уже потратили огромное количество времени и сил, чтобы найти и исправить все самые ужасные баги самостоятельно? Разве это не эпический провал?

Я не преувеличиваю, заметьте. Многие разработчики не совсем понимают, как правильно использовать анализаторы кода. Мы получаем множество писем, в которых говорится что-то вроде этого: «Классная штука. Собираюсь использовать его перед выпуском». Это печально. Вот почему я постоянно стремлюсь внести свет в темную страну ошибок и программистов, не желающих думать.

Я не утверждаю, что анализатор может найти все возможные ошибки. Он может найти только некоторые из них. Но сделает это сразу — как только программист закончит писать новый блок кода.

Идея статического анализа заключается в том, что многие ошибки и опечатки можно обнаружить на самой ранней стадии разработки. Используя его регулярно, вы можете значительно сократить время, которое в противном случае тратили бы на поиск и устранение дефектов в вашем коде.

Нужна PVS-Studio!

Чтобы убедить вас раз и навсегда, приведу один пример из жизни. Недавно мы анализировали и исправляли ошибки в игровом движке Unreal Engine. Большинство ошибок были некритичными, что естественно, ведь иначе никто не стал бы использовать Unreal Engine для разработки своего ПО.

В какой-то момент мы добились вывода 0 сообщений. Но разработчики естественно продолжали дорабатывать и редактировать код. Итак, анализатор начал выдавать новые предупреждения на свежий код. Мы могли буквально в режиме реального времени видеть, как в код попадают новые баги. Вероятно, через несколько дней они будут устранены, но зачем все эти хлопоты, когда анализатор может их сразу уловить? Вот один из таких багов:

static void GetArrayOfSpeakers(....)
{
  Speakers.Reset();
  uint32 ChanCount = 0;
  // Build a flag field of the speaker outputs of this device
  for (uint32 SpeakerTypeIndex = 0;
       SpeakerTypeIndex < ESpeaker::SPEAKER_TYPE_COUNT,    //<==
       ChanCount < NumChannels; ++SpeakerTypeIndex)
  {
    ....
  }
  check(ChanCount == NumChannels);
}

Вместо оператора && программист случайно поставил запятую. Нельзя назвать этот баг незначительным. Он попал в систему контроля версий и, я уверен, доставил бы много неприятностей, если бы не PVS-Studio, который был начеку и его поймал.

Подробнее о нашем опыте работы с компанией Epic Games читайте в статье Как команда PVS-Studio улучшала код Unreal Engine.

Вывод

Я предлагаю вам пойти и попробовать наш анализатор кода PVS-Studio на вашем проекте прямо сейчас. Скачать его можно здесь. Интерфейс инструмента довольно прост, но я рекомендую вам ознакомиться со статьей PVS-Studio для Visual C++ за полезными советами по использованию анализатора. Например, мало кто знает, как легко и быстро исключить сторонние библиотеки из анализа.

Если вы используете makefile или собственную систему сборки для сборки своих проектов, вы можете использовать приложение PVS-Studio Standalone для их анализа. Этот инструмент использует механизм мониторинга запуска компилятора.

Если вас пугает огромное количество диагностических сообщений, выводимых при первых запусках, попробуйте наш новый режим маркировки сообщений, использующий специальную базу данных. Короче говоря, идея в том, что все сообщения считаются неактуальными и не отображаются в окне вывода сообщений; вы будете видеть только сообщения о свежем коде. Я уверен, что вы очень скоро узнаете, насколько удобен и полезен статический анализ кода при регулярном использовании. Подробнее читайте в статье «Интеграция статического анализа в проект».

Удачи!