Общаясь с людьми на форумах, я заметил несколько устойчивых заблуждений относительно методологии статического анализа.
Хочу развеять следующие мифы:
- Статический анализатор является продуктом одноразового использования.
- Профессиональные разработчики не допускают глупых ошибок.
- Динамический анализ лучше статического анализа.
- Программисты хотят добавлять в статический анализатор свои правила.
Миф номер один: статический анализатор — продукт одноразового использования
Вот как выглядит подобное заявление при обсуждении на форуме (собирательное изображение): Имея триальную/взломанную версию, вы сможете бесплатно запустить все свои проекты, найти несколько старых ошибок и, по сути, на какое-то время успокоиться на этом.Все довольны.
Люди проверяли.
Разработчики анализатора так и не узнали, что их обманули и ограбили.
В данном случае обманул себя программист, а не создатели инструмента.
Он получил лишь видимость выгоды от проделанной работы, а не реальную выгоду.
Пока мне не удалось донести эту идею, но я буду продолжать пытаться это сделать.
От одиночных запусков статического анализатора нет никакой пользы.
Аналогия: Установите уровень предупреждения компилятора /W0. И мы разрабатываем проект. Ругаемся, исправляем глупые ошибки и опечатки, тестируем больше и дольше.
Затем мы время от времени включаем /W3 и боремся с предупреждениями, а затем снова возвращаемся к /W0. При этом мы бесстрашно и долго искали в отладчике то, что может предложить компилятор на уровне /W3, и тратили на это в 10-100 раз больше времени.
Кроме того, обратите внимание, что теперь программисту не понравится результат /W3. Ведь он уже исправил почти все ошибки путём тестирования и отладки.
Компилятор на уровне /W3 теперь выдает в основном ложные срабатывания.
Теперь вернемся к статическому анализу.
Картина полностью идентична.
Редкий запуск анализатора дает много ложных срабатываний.
Реальных ошибок мало, так как они обнаруживаются другими методами.
Как и в случае с переключателем /W3, использование статического анализа наиболее полезно при регулярном использовании.
Кстати, статический анализ — это своего рода расширение предупреждений, выдаваемых компилятором.
Многие диагностики, которые когда-то были реализованы в старых анализаторах, постепенно переходят в компилятор.
Конечно, анализаторы всегда будут впереди компиляторов с точки зрения диагностики.
Они созданы специально для этого.
У компилятора больше забот и, кроме того, к нему предъявляются более жесткие требования к производительности.
Некоторые в пылу дискуссии отвечают так: Идея актуальна для начинающих студентов.
Для специалистов это уже не так важно.
Если поставлю /W0, хуже не напишу.
Нам нужно улучшить наш стиль программирования, а не увеличивать количество инструментов-костылей.
Я полностью согласен со всем написанным выше.
Но давайте поиграемся и изменим текст вот так: Идея актуальна для начинающих водителей.
Для профессионалов это уже не так важно.
Если я не буду пристегиваться ремнем безопасности за рулем, я не буду водить хуже.
Необходимо совершенствовать свой стиль вождения, а не увеличивать количество составляющих страховки.
И снова не поспоришь.
Однако любой рационально мыслящий водитель понимает, что пристегиваться все равно полезно.
Статический анализ также полезен.
Ведь даже самый опытный программист не застрахован от ошибок и опечаток.
И примеры, приведенные в этот статья хорошее тому подтверждение.
Конечно, все профессиональные программисты уверены, что таких глупых ошибок они не допускают, но об этом позже.
Миф второй: профессиональные разработчики не допускают глупых ошибок.
Миф второй: «Профессиональные разработчики не допускают глупых ошибок, которые в основном и выявляют статические анализаторы кода».
Вот как выглядит подобное заявление при обсуждении на форуме (собирательное изображение): У меня, профессионального разработчика, уже N лет не было проблем с повреждением памяти, временем жизни объектов и так далее.
Статический анализ — это инструмент Макдональдса, а здесь (на профессиональном форуме) есть гики.
Сейчас мои основные проблемы — это сложные для тестирования алгоритмы и интеграция с другими разработчиками, использующими неявные контракты на состояния объектов.
Звучит так, будто проблемы опечаток и ошибок по неосторожности — удел студентов.
Профессиональные разработчики их уже давно не делают, и основные неприятности вызывают такие сложные ошибки, как проблемы синхронизации или сложные алгоритмы обработки данных.
Это не верно.
Все программисты делают глупые ошибки.
Я знаю, ты меня не услышал.
Еще раз повторю еретические мысли.
Все программисты делают глупые ошибки.
Неважно, насколько они профессиональны.
Люди склонны совершать ошибки.
И чаще всего эти ошибки простые.
Программисты очень недружелюбно отнеслись к моим высказываниям об ошибках.
По их мнению, именно они уже очень давно не допускали подобных ошибок.
Я думаю, здесь действует интересный аспект психики, отфильтровывающий воспоминания о неинтересных моментах программирования.
Давайте немного отвлечемся и вспомним, почему различные гороскопы так живучи.
Первая причина – очень расплывчатые формулировки, которые человеку легко адаптировать под себя.
Но нас интересует вторая составляющая.
Люди не помнят тех случаев, когда предсказание не сбывалось.
Но они очень хорошо помнят и пересказывают те случаи, когда их жизненная ситуация совпадала с ситуацией, описанной в гороскопе.
В результате получается, что, говоря и вспоминая о гороскопе, мы находим N подтверждений того, что гороскопы работают, и не помним N*10 случаев, когда гороскоп не сработал.
Нечто подобное происходит и с программистом, когда он ищет ошибки.
Он хорошо помнит сложные и интересные ошибки.
Возможно, обсудите их с коллегами или напишите сообщение в блоге.
Когда он заметит, что вместо переменной «АВ» написал «БА», он просто исправит ошибку, и этот факт тут же исчезнет из его памяти.
Фрейд обратил внимание на следующую особенность памяти: человеку свойственно запоминать положительные высказывания о себе и забывать отрицательные.
Если человек борется с трудной ошибкой в алгоритмической задаче, то, исправив ее, он считает себя героем.
Об этом стоит помнить и даже рассказывать другим.
Когда он находит глупый баг, у него нет ни причины, ни желания вспоминать о нем.
Какие у меня есть доказательства? Хотя большинство опечаток и ошибок исправлено, некоторые из них все равно остаются незамеченными.
И множество примеров таких ошибок можно найти в этот статья.
В статье наглядно показано, что ошибки допускали не новички, а квалифицированные программисты.
Заключение.
Программисты тратят гораздо больше времени на исправление опечаток, чем они думают. Инструменты статического анализа могут существенно сэкономить усилия разработчиков, выявив некоторые из этих ошибок еще до этапа тестирования.
Миф третий – динамический анализ лучше статического
Миф третий: «Динамическая проверка с помощью таких инструментов, как valgrind для C/C++, гораздо более продуктивна, чем статический анализ кода».Заявление весьма странное.
Динамический и статический анализ — это просто две разные методологии, дополняющие друг друга.
Программисты, кажется, это понимают. Но я снова и снова слышу, что динамический анализ лучше статического.
Перечислю сильные стороны статического анализа кода.
Диагностика всех веток программы На практике динамический анализ не может охватить все ветви программы.
После этих слов поклонники valgrind говорят, что необходимо сделать правильные тесты.
Теоретически они правы.
Но любой, кто пробовал это сделать, понимает сложность и объем работы.
На практике даже хорошие тесты покрывают не более 80% программного кода.
Особенно это заметно в участках кода, обрабатывающих нештатные/аварийные ситуации.
Если вы возьмете старый проект, то большую часть ошибок статический анализатор обнаружит именно в этих местах.
Причина в том, что хоть проект и старый, но эти направления практически не опробованы.
Вот очень короткий пример, показывающий, что я имею в виду (проект FCE Ultra):
fp = fopen(name,"wb"); int x = 0; if (!fp) int x = 1;Флаг «x» не будет установлен в единицу, если файл не был открыт. Именно из-за подобных ошибок, когда в программах что-то идет не так, они аварийно завершают работу или выдают бессмысленные сообщения вместо адекватных сообщений об ошибках.
Масштабируемость Для того, чтобы регулярно проверять крупные проекты динамическими методами, необходимо создать специальную инфраструктуру.
Нужны специальные тесты.
Вам необходимо параллельно запускать несколько экземпляров приложения с разными входными данными.
Статический анализ масштабируется в несколько раз проще.
Как правило, для программы статического анализа достаточно обеспечить машину с большим количеством ядер.
Анализ более высокого уровня Преимущество динамического анализатора состоит в том, что он знает, какая функция с какими аргументами вызывается.
В результате он может проверить правильность вызова.
Статический анализ в большинстве случаев не может это выяснить и проверить значения аргументов.
Это минус.
Но статический анализ проводит анализ более высокого уровня, чем динамический анализ.
И это позволяет ему искать правильные с точки зрения динамического анализа вещи.
Простой пример (проект ReactOS):
void Mapdesc::identify( REAL dest[MAXCOORDS][MAXCOORDS] ) { memset( dest, 0, sizeof( dest ) ); for( int i=0; i != hcoords; i++ ) dest[i][i] = 1.0; }С точки зрения динамического анализа здесь все в порядке.
Но статический анализ убьет тревога , так как очень подозрительно, что в нуле обнуляется столько байтов в массиве, сколько байтов состоит из указателя.
Или вот еще пример из проекта Clang:
MapTy PerPtrTopDown; MapTy PerPtrBottomUp; void clearBottomUpPointers() { PerPtrTopDown.clear(); } void clearTopDownPointers() { PerPtrTopDown.clear(); }Что здесь может заподозрить динамический анализатор? Ничего.
И статический анализатор может заподозрить неладное.
Ошибка здесь в том, что внутри методаclearBottomUpPointers() должно быть: "PerPtrBottomUp.clear();".
Миф номер четыре: программисты хотят добавлять в статический анализатор свои правила
Миф четвертый: «Статический анализатор должен иметь возможность добавлять собственные правила.Программисты хотят добавить свои собственные правила».
Нет, они этого не делают. Фактически, они хотят решить некоторые проблемы поиска определенных языковых конструкций.
И это не то же самое, что создание диагностических правил.
Мой ответ всегда заключался в том, что реализация собственных правил — это не то, чего хотят программисты.
И другой альтернативы, кроме реализации диагностики разработчиками анализатора по просьбе программистов, я не увидел( статья на эту тему ).
Недавно у меня был близкий разговор с Дмитрием Петуниным.
Он является руководителем отдела тестирования компиляторов и разработки инструментов проверки программ в Intel. Он расширил мое понимание этой темы и озвучил идею, над которой я думал, но так и не сформулировал в окончательном варианте.
Дмитрий подтвердил мою уверенность в том, что программисты не будут писать диагностические правила.
Причина очень проста – это очень сложно.
Ряд инструментов статического анализа имеют возможность расширять набор правил.
Но сделано это больше для галочки или для удобства самих создателей инструмента.
Для разработки новой диагностики необходимо очень глубокое погружение в тему.
Если за это возьмется энтузиаст без опыта, то его правила принесут мало практической пользы.
На этом мое понимание вопроса закончилось.
Дмитрий, имея большой опыт, расширил его.
Кратко ситуация выглядит так.
Программистам очень хочется искать в своем коде какие-то закономерности/ошибки.
Им это действительно нужно.
Например, человеку нужно найти все явные приведения типа int к типу float. Эту проблему невозможно решить с помощью таких инструментов, как grep. Ведь в конструкции типа «float(P-> FOO())» неизвестно, какой тип вернет функция FOO().
В этот момент программист приходит к выводу, что можно реализовать поиск таких структур, добавив собственную проверку в статическом анализаторе.
В этом и заключается ключевой момент. Человеку не нужно создавать свои правила анализа.
Ему нужно решить личную проблему.
Ему нужна очень небольшая задача с точки зрения механизмов статического анализа.
Это то же самое, что использовать автомобиль для прикуривания сигарет от прикуривателя.
Именно поэтому и Дмитрий, и я не поддерживаем идею предоставления пользователю API для работы с анализатором.
Это чрезвычайно сложная задача с точки зрения развития.
И при этом человек вряд ли употребит более 1% его.
Иррационально.
Разработчику проще и дешевле реализовать пожелания пользователей, чем создавать сложный API для модулей расширения или создавать специальный язык описания правил.
Читатель отметит: «тогда откройте в API только 1% функционала и все будут счастливы».
Да все правильно.
Но посмотрите, как сместились акценты.
Разрабатывая наши правила, мы пришли к выводу, что на самом деле достаточно инструмента, похожего на grep, но с некоторой дополнительной информацией о программном коде.
Такого инструмента пока нет. Если вы хотите решить проблему, можете написать мне и мы попробуем реализовать ее в анализаторе PVS-Studio. Например, недавно мы реализовали несколько запросов на поиск явного преобразования типов: В2003 , В2004 , В2005 г.
.
Воплотить такие желания в жизнь нам гораздо проще, чем создавать и поддерживать открытый интерфейс.
Это проще для самих пользователей.
Кстати, возможно, со временем такой инструмент появится в составе Intel C++.
Дмитрий Петунин рассказал, что они обсуждали возможность создания grep-подобного инструмента, обладающего знаниями о структуре кода и типах переменных.
Но это обсуждалось абстрактно.
Я не знаю, есть ли вообще планы создать что-то подобное или нет. Теги: #статический анализ кода #статический анализ #c plus plus #разрушители мифов
-
Восстание Voip
19 Oct, 24 -
«Гастромаркет» — Сервис По Поиску Кулинаров.
19 Oct, 24 -
Зевс Троян Первое Знакомство
19 Oct, 24 -
Появился Более Четкий Рендер Htc Bravo
19 Oct, 24