Вебинар: Использование статических анализаторов кода при разработке безопасного ПО - 19.12
Желание пользователей сравнить между собой разные анализаторы кода понятно и естественно. Однако реализовать это желание совсем не так просто как может показаться на первый взгляд. Дело в том, что непонятно какие конкретно факторы между собой сравнивать.
Если отбросить уж совсем нелепые идеи типа "сравнить количество диагностируемых ошибок" или "сравнить количество сообщений, которые выдал инструмент", то даже разумный параметр "соотношение сигнал/шум" не кажется идеальным критерием оценки анализатора кода.
Вы сомневаетесь, что сравнивать указанные параметры бессмысленно? Приведем несколько примеров.
Рассмотрим такую простую на первый взгляд характеристику, как количество диагностических проверок. Кажется, что чем их больше, тем лучше. Но для конечного пользователя, использующего определенный набор операционных систем и компиляторов, общее количество правил ничего не значит. Правила диагностики, актуальные для систем, библиотек и компиляторов, которые он не использует, ничего ему не дают. Они даже мешают ему, загромождая систему настройки и документацию, усложняют использование и внедрение инструмента.
Здесь уместна следующая аналогия. Человек заходит в магазин, чтобы купить обогреватель. Ему интересен отдел бытовой техники и если в этом отделе большой выбор, то это хорошо. А вот другие отделы ему не интересны. Нет ничего плохого, если в этом магазине можно купить надувную лодку, сотовый телефон и стул. Но наличие отдела надувных лодок никак не улучшает ассортимент обогревателей.
Возьмем, например, инструмент Klocwork, поддерживающий большое количество разнообразных систем, в том числе и экзотических. В одной из таких систем есть компилятор, который "проглатывает" вот такой вот код:
inline int x;
Анализатор Klocwork имеет диагностическое сообщение, позволяющее обнаружить эту аномалию в коде: "The 'inline' keyword is applied to something other than a function or method". Получается, что вроде хорошо, что есть такая диагностика. Но, скажем, разработчик, использующий компилятор Microsoft Visual C++ или другой адекватный компилятор, никакой пользы от этой диагностики не имеет. Visual C++ просто не компилирует такой код: "error C2433: 'x' : 'inline' not permitted on data declarations".
Другой пример. Некоторые компиляторы плохо поддерживают тип bool. Поэтому Klocwork может предупредить о ситуации, когда член класса имеет тип bool: "PORTING.STRUCT.BOOL: This checker detects situations in which a struct/class has a bool member".
Написали bool в классе, вот ужас то... Понятно, что пользу от такого диагностического сообщения может получить крайне малый процент разработчиков.
Подобных примеров можно привести очень много. И получается, что общее количество диагностических правил никак не связано с тем, какое количество ошибок анализатор обнаружит в определенном проекте. Анализатор, реализующий 100 диагностик и ориентированный для Windows-приложений, может найти намного больше ошибок в проекте, собираемом Microsoft Visual Studio, чем кросс-платформенный анализатор, реализующий 1000 диагностик.
Итак, мы получили, что нельзя сравнивать полезность анализатора по такому параметру, как число диагностических проверок.
Можно сказать: "Давайте тогда сравнивать количество проверок, актуальных для определенной системы. Например, отберем все правила, которые позволяют искать ошибки в Windows программах". Но и такой подход не работает по двум причинам:
Во-первых, нередко бывает так, что в каком-то анализаторе проверка реализована одним диагностическим правилом, а в другом – несколькими правилами. И если сравнивать по количеству правил диагностики, то вроде бы один из анализаторов лучше, хотя они одинаковы по обнаруживаемым ошибкам.
Во-вторых, реализация тех или иных диагностик может быть различного качества. Например, почти во всех анализаторах есть поиск "магических чисел". Но, к примеру, в одном анализаторе могут обнаруживаться только магические числа, опасные с точки зрения переноса кода на 64-битные системы (4, 8, 32 и т.п.), то в другом – просто все магические числа (1, 2, 3 и т.п.). И просто в таблице сравнения в графе "поиск магических чисел" поставить и там, и там "плюсик" будет недостаточно.
Еще очень любят приводить такую характеристику, как скорость работы инструмента, или количество обрабатываемых строк кода в минуту. Но ведь и она не имеет практического смысла. Нет никакой связи между скоростью работы анализатора кода и скоростью анализа проекта человеком! Во-первых, нередко запуск анализа кода производится автоматически во время ночных сборок. И важно просто "успеть" проверить до утра. А, во-вторых, часто забывают при сравнении про такой параметр как удобство использования. Впрочем, давайте разберемся с этим вопросом подробнее.
Дело в том, что очень большую роль в реальном применении анализаторов кода играет то, насколько инструмент удобно применять...
Недавно мы проверяли проект eMule двумя анализаторами кода, оценивая удобство этой операции. Одним из них был статический анализатор, встроенный в некоторые редакции Visual Studio. Другой же – наш PVS-Studio. И мы сразу же увидели несколько проблем в работе с анализатором кода, встроенным в Visual Studio. Причем эти проблемы не относятся к самому качеству анализа и скорости работы.
Первая проблема – это невозможность сохранить список сообщений от анализатора для дальнейшей работы с ним. Например, при проверке встроенным анализатором eMule я получил две тысячи сообщений. Вдумчиво обработать их все сразу невозможно, поэтому приходится возвращаться к ним в течение нескольких дней. Однако невозможность сохранить результаты анализа приводит к тому, что приходится перепроверять проект каждый раз, что сильно утомляет. В PVS-Studio есть возможность сохранить результаты анализа для того, чтобы вернутся к работе с ними позже.
Вторая проблема связана с тем, как реализована обработка дублирующихся сообщений анализатора. Речь о диагностике проблем в заголовочных файлах (.h-файлах). Пусть анализатор обнаружил проблему в .h-файле, который включается в десять .cpp-файлов. Анализируя каждый из этих десяти .cpp-файлов встроенный в Visual Studio анализатор выдает одно и то же сообщение про проблему в .h-файле десять раз! Конкретный пример. При проверке eMule сообщение
c:\users\evg\documents\emuleplus\dialogmintraybtn.hpp(450):
warning C6054: String 'szwThemeColor' might not be zero-terminated:
Lines: 434, 437, 438, 443, 445, 448, 450
выдавалось более десяти раз. Из-за этого захламляются результаты анализа, и просматривать приходится практически постоянно одни и те же сообщения. Надо сказать, что в PVS-Studio с самого начала дубликаты сообщений не показывались, а фильтровались.
Третья проблема – выдача сообщений на проблемы в системных подключаемых файлах (из папок вида C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include). Встроенный в Visual Studio анализатор не стесняется клеймить позором системные заголовочные файлы, хотя практического смысла в этом не много. Опять же пример. Многократно встречалось при проверке eMule одно и то же сообщение про системные файлы:
1>c:\program files (x86)\microsoft
sdks\windows\v7.0a\include\ws2tcpip.h(729):
warning C6386: Buffer overrun: accessing 'argument 1',
the writable size is '1*4' bytes,
but '4294967272' bytes might be written:
Lines: 703, 704, 705, 707, 713, 714, 715, 720,
721, 722, 724, 727, 728, 729
Все равно править системные файлы никто не будет. Так зачем же на такие файлы "ругаться"? PVS-Studio никогда не ругался на системные файлы.
Сюда же можно отнести невозможность указать инструменту, чтобы он не проверял некоторые файлы по маске. Например, все файлы "*_generated.cpp" или "c:\libs\". В PVS-Studio указать исключаемые файлы можно.
Четвертая проблема затрагивает собственно работу со списком сообщений от анализатора кода. В любом анализаторе кода, конечно же, можно отключить любые диагностические сообщения по коду. Только вот делать это можно с разным уровнем удобства. Точнее вопрос в том нужно ли перезапускать анализ для сокрытия лишних сообщений по коду или не нужно. В анализаторе кода из Visual Studio нужно переписать коды отключаемых сообщений в настройки проекта, затем перезапустить анализ. При этом, разумеется, сразу же все "лишние" диагностики указать вряд ли удастся, поэтому перезапуск придется повторить несколько раз. В PVS-Studio можно легко скрывать и показывать сообщения по коду без перезапуска и намного более удобно.
Пятая проблема – фильтрация сообщений не только по коду, но и по тексту. Например, полезно скрыть все сообщения, содержащие "printf". Во встроенном в Visual Studio анализаторе такой возможности нет, в PVS-Studio есть.
Наконец, шестая проблема – это насколько удобно можно указать инструменту на то, что это сообщение – ложное срабатывание. Механизм с #pragma warning disable, используемый в Visual Studio, позволяет спрятать сообщение только при перезапуске анализа. В отличие от механизм в PVS-Studio, где сообщения можно пометить как "False Alarm" и скрыть без перезапуска анализа.
Все шесть перечисленных проблем хоть и не затрагивают качество собственно анализа кода, но имеют очень большое значение. Ведь удобство использования инструмента – это тот интегральный показатель, от которого зависит: дойдет ли дело собственно до оценки качества анализа или нет.
Итого, получаем следующую картину. Проект eMule проверяется статическим анализатором, встроенным в Visual Studio, в несколько раз быстрее, чем это делает PVS-Studio. Но для работы с анализатором Visual Studio понадобилось 3 дня (на самом деле меньше, но приходилось переключаться на другие задачи, чтобы отдохнуть). А для работы с PVS-Studio потребовалось только 4 часа.
Примечание. По поводу количества найденных ошибок – оба анализатора показали приблизительно равнее результаты, и нашли в этом проекте одни и те же ошибки.
Сравнение статических анализаторов между собой – очень сложная и комплексная задача. И ответить на вопрос какой инструмент лучше ВООБЩЕ – нельзя. Можно только говорить о том, какой инструмент лучше для конкретного проекта и пользователя.
0