Метод выявления ошибок в коде при помощи комментирования

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

Концепция

Написать достаточно большой код без единой ошибки – весьма приятно. Но, к сожалению, так выходит не всегда. Есть даже шутка, что ни одна программа не была написана без единой ошибки. Я не рассматриваю здесь ошибки, которые приводят к неверному исполнению кода. Здесь пойдёт речь об ошибках, из-за которых становится невозможной компиляция.

Весьма распространённые ошибки – вставка лишней скобки в сложном условии, нехватка скобки, не выставление двоеточия, запятой (при объявлении переменных) и т. д. Часто при компиляции мы можем сразу увидеть, в какой строке допущена подобная ошибка. Но бывают и случаи, когда найти такую ошибку не так просто. Ни компилятор, ни зоркий глаз нам не могут помочь сходу найти ошибку. В таких случаях, как правило, начинающие (и не очень) программисты начинают «обходить» весь код, пытаясь визуально определить ошибку. И снова, и снова, пока нервы не иссякнут, и не будет сказано «проще заново написать!».

Алгоритм поиска ошибок

Поиск ошибок обычно сводится к определению участка кода, где допущена ошибка, а затем, в этом участке, визуально находится ошибка. Думаю, вряд ли кто-то будет сомневаться в том, что исследовать «на глаз» 5-10 строчек кода проще и быстрей, чем 100-500.

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

Весьма важно правильно определять участки кода, которые необходимо комментировать. Если это условие (или иная логическая конструкция) – то оно должно комментироваться полно. Если комментируется участок кода, где объявляются переменные, важно, чтобы не был открыт участок, где происходит обращение к этим переменным. Иначе говоря, комментирование должно применяться по логике программирования. Несоблюдения такого подхода приводит к возникновению новых, вводящих в заблуждение, ошибок при компиляции.

Пример

Приведу пример практического поиска ошибки в коде. Допустим, у нас есть некоторый код:

При попытке его компиляции мы видим сообщение об ошибке:

Оперативно определить участок кода, где допущена ошибка, не представляется возможным. Прибегаем к комментированию. Комментируем все логические конструкции:

Компиляция будет происходить благополучно, пока мы не дойдём до участка кода:

Следовательно, ошибка именно в этой логической конструкции. При детальном «осмотре» данного участка кода, можно увидеть, что поставлена лишняя фигурная скобка в данной конструкции:

Если убрать её, код благополучно откомпилируется.

Заключение

На практическом примере было показано, как именно используется данный алгоритм поиска ошибок. В данном примере используется весьма немаленький код (194 строки), и на его «обход» могло бы уйти достаточно много времени. Именно возможность комментирования экономит достаточно много времени у многих программистов, которые сталкиваются с задачей поиска ошибок.

VivaMP, система выявления ошибок в коде параллельных программ на языке С++, использующих OpenMP

В статье приводятся результаты исследований ошибок, которые допускают программисты, использующие С++ и OpenMP. Для автоматического обнаружения этих ошибок предлагается использование статического анализа. Описывается интегрирующийся в среду Visual Studio анализатор VivaMP, реализующий поставленную задачу.

Поддержка OpenMP была прекращена в PVS-Studio после версии 5.20. По всем возникшим вопросам вы можете обратиться в нашу поддержку.

В настоящее время программные продукты Viva64 и VivaMP включены в состав PVS-Studio и более не распространяются как отдельные приложения. Используйте программу PVS-Studio для получения необходимых возможностей проверки кода.

1. Введение

По оценкам компании Evans Data, проводящей опросы среди разработчиков ПО, общее количество программистов в мире к 2009 году составит 17 миллионов человек[1]. На сегодняшний день 40% из них используют язык С++, причем около 70% разработчиков занимаются разработкой многопоточных приложений сейчас или планируют начать ее в течение года. По данным того же опроса, 13,2% этих разработчиков считают, что главной проблемой таких разработок является нехватка программных средств для создания, тестирования и отладки параллельных приложений. Следовательно, в решении задачи автоматического поиска ошибок в исходном коде непосредственно заинтересованы примерно 630.000 программистов.

Целью работы является создание статического анализатора кода, предназначенного для автоматического обнаружения таких ошибок. В исследовании рассматривался язык C++, поскольку к коду именно на этом языке чаще всего предъявляются требования высокой производительности. Так как поддержка технологии OpenMP встроена в Microsoft Visual Studio 2005 и 2008, и некоторые специалисты считают, что именно технология OpenMP вскоре приобретет наибольшую популярность [3], рассматривалась именно эта технология (помимо C++ применяемая также для языков C и Fortran).

Анализ обзоров отладчиков для параллельных программ показывает, что ситуация в этой сфере до сих пор далека от идеала. Применительно к отладке программ, написанных на С++ и использующих OpenMP, как правило, упоминаются TotalView и Intel Thread Checker. Однако, оба эти инструмента предназначены для динамического использования. До недавнего времени направление статического анализа OpenMP программ было практически не освоено. В качестве примера можно привести, пожалуй, только достаточно качественную диагностику, выполняемую компилятором Sun Studio. Статический анализатор VivaMP заполнил это нишу.

2. Применение статического анализа для отладки параллельных программ

Большинство существующих на данный момент средств отладки параллельных программ являются динамическими анализаторами, предполагающими непосредственное выполнение анализируемой программы. Такой подход имеет свои преимущества, но у него имеются и недостатки.

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

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

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

Статический анализ позволяет просмотреть весь исходный код приложения, не требует от программиста никаких дополнительных усилий и приводит к обнаружению опасных фрагментов кода. Недостаток статического анализа заключается в том, что он не позволяет проверить поведение, зависящее от пользователя. Еще одной проблемой являются ложные срабатывания, уменьшение количества которых требует дополнительных усилий при разработке анализатора. Подробнее вопрос применения статического анализа при разработке параллельных программ рассматривается в статье [4].

В анализаторе VivaMP используется анализ с обходом дерева кода (tree walk analysis). Помимо этого, существуют и другие виды статического анализа, предполагающие моделирование выполнения программы, расчет возможных значений переменных и путей выполнения кода. Статический анализ как средство диагностики ошибок в параллельных программах был выбран потому, что данный подход позволяет находить многие ошибки, не диагностируемые динамическими анализаторами. Теперь рассмотрим сами ошибки подробнее.

3. Диагностируемые ошибки

Список возможных ошибок, не обнаруживаемых компилятором Visual Studio, был составлен в результате исследования работ, посвященных параллельному программированию с использованием OpenMP, а также на основе личного опыта авторов. Все ошибки можно разделить на четыре основные категории:

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

Приведем примеры ошибок каждого вида и их краткое описание.

3.1. Отсутствие ключевого слова parallel

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

Пример ошибки, вызванной отсутствием ключевого слова parallel:

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

3.2. Неправильное применение блокировок

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

Пример некорректного использования блокировок:

Первый пример некорректного кода приведет к ошибке во время выполнения программы (поток не может освободить переменную, занятую другим потоком). Второй пример иногда будет работать корректно, а иногда будет приводить к зависанию. Зависеть это будет от того, какой поток завершается последним. Если последним будет завершаться поток, в котором выполняется блокировка переменной без ее освобождения, программа будет выдавать ожидаемый результат. Во всех остальных случаях будет возникать бесконечное ожидание освобождения переменной, захваченной потоком, работающим с переменной некорректно.

Теперь рассмотрим другую ошибку более подробно и приведем соответствующее правило для анализатора.

3.3. Незащищенный доступ к общей памяти

Эта ошибка может встретиться в любой параллельной программе, написанной на любом языке. Также она называется состоянием гонок (race condition) и суть ее заключается в том, что значение общей переменной, изменяемой одновременно из нескольких потоков, в результате может оказаться непредсказуемым. Рассмотрим простой пример для С++ и OpenMP.

Пример состояния гонок:

Эту ошибку также можно обнаружить средствами статического анализатора. Рассмотрим правило, по которому статический анализатор VivaMP сможет обнаружить эту ошибку:

Опасными следует считать инициализацию или модификацию объекта (переменной) в параллельном блоке, если объект относительно этого блока является глобальным (общим для потоков).

К глобальным объектам относительно параллельного блока относятся:

Объект может быть как переменной простого типа, так и экземпляром класса. К операциям изменения объекта относится:

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

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

В заключение этого раздела отметим, что более полный список обнаруженных в результате исследований ошибок и их более подробные описания можно найти в статье [5]. Помимо этого, пример программы, демонстрирующей ошибочные и исправленные версии проблемного кода, можно найти на сайте проекта VivaMP.

Теперь перейдем к описанию самого анализатора.

4. Анализатор VivaMP

a0059_VivaMP_detecting_errors_ru/image1.png

Рис. 1. Интерфейс VivaMP

На данный момент выпущена первая версия анализатора, информацию о которой можно получить по адресу /ru/vivamp-tool/. Первая версия VivaMP диагностирует 19 ошибок, однако, количество собранного материала и результаты экспериментов позволяют существенно увеличить это число (как минимум в два раза) в последующих версиях. Ниже перечислены краткие описания диагностируемых ошибок:

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

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

Источники:

https://www. mql5.com/ru/articles/1547

https://pvs-studio. com/ru/a/0059/

Понравилась статья? Поделиться с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: