10 полезных советов для отладки и устранения неполадок в программировании

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

Ваш набор инструментов для борьбы с ошибками

1. Оператор печати

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

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

2. Отладчик

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

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

3. Система отслеживания ошибок

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

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

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

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

4. Верификация программ

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

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

5. Контроль версий

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

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

6. Модульность

Плохо спроектированный код – это главный источник трудно исправляемых ошибок. Если код легко понять, и он может быть « выполнен » в уме или на бумаге, есть большая вероятность, что программисты смогут быстро находить и исправлять ошибки.

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

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

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

7. Автоматизированные тесты

Модульные тесты и другие типы автоматизированных тестов идут рука об руку с модульным программированием.

Автоматизированный код – это участок кода, который выполняет программу с определенными входными параметрами и проверяет, соответствует ли поведение программы ожидаемому.

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

8. Метод «Плюшевый мишка» (или отладка «Резиновая уточка»)

Если верить легендам программирования Брайану Кернигану и Робу Пайку (Brain Kernighan и Rob Pike), отладка по типу «Резиновая уточка» возникла в университетском компьютерном центре, где студенты должны были садиться напротив плюшевого мишки и объяснять ему их ошибки, прежде чем обращаться за помощью к живому человеку.

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

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

9. Пишите комментарии к коду

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

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

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

10. Пишите документацию

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

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

На пути к мастерству: избавляемся от ошибок

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

Какими из этих 10 средств отладки вы пользуетесь сейчас? Какими вы могли бы начать пользоваться с сегодняшнего дня? Какие из этих инструментов требуют времени на практику и освоения новых навыков?

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

AppChecker — инструмент статического анализа

В настоящее время информационные технологии проникли практически во все сферы ведения бизнеса. Для решения бизнес-задач создаются крупные, распределенные, постоянно модифицируемые информационные системы с тенденцией к усложнению. Они могут иметь в своем составе как готовые решения, так и внешние IT-сервисы (SaaS), собственные и заказные разработки, бесплатные продукты с открытым исходным кодом (open source). Проблемы в их работе приводят к нарушению информационной безопасности, а как следствие, к финансовым и репутационным потерям бизнеса. По данным исследования [9], последние четыре года финансовые потери бизнеса растут из-за кибератак. Повышение сложности используемых программных комплексов, их включение в контур систем управления государством и производством промышленной продукции требуют постоянного совершенствования методов тестирования, испытаний и контроля программного обеспечения.

Большинство методов анализа ПО, применяемого в аудите безопасности программных систем, можно разделить на две группы (рис. 1) — динамические методы (функциональное тестирование) и статические методы (структурное тестирование).

классификация видов анализа программ

Рис. 1. Примерная классификация видов анализа программ (цветом выделены те виды анализа, о которых говорится в данной статье)

Динамический анализ представляет собой совокупность всех методов анализа программного обеспечения, реализуемых с помощью программ на реальном или виртуальном процессоре. Такие способы наиболее востребованы при исследовании программ методом «черного ящика» (black box), когда имеется доступ лишь к внешним интерфейсам программного обеспечения без учета их структуры, внутренних интерфейсов и состояния [5]. Этот подход не всегда эффективен при поиске ошибок, связанных с комбинациями редко используемых входных данных, а также при выявлении скрытого программного функционала (программных закладок), внесенного туда преднамеренно.

Для своей работы статический анализ не требует реализации програм­много кода и допускает полную или частичную автоматизацию процесса, но предполагает доступ не только к загрузочным и объектным модулям, но и к исходным текстам программной системы, к информации, связанной со средой компиляции и выполнением программных компонентов [3–5].

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

Методы статического анализа кода

Статический анализ исходных текстов программного обеспечения тесно связан с принципами работы компиляторов. Многие подходы такого анализа основаны на некоторых элементах компиляции, в частности, промежуточное представление исходных текстов в статических анализаторах эволюционирует совместно с развитием теории компиляторов. Современные методы анализа в целях унификации алгоритмов используют различные модели представления кода, например лексический разбор, синтаксическое дерево, дерево Канторовича, графы потока данных и управления и т. д. [1].

Подход, получивший название сигнатурного анализа, подразумевает поиск дефектов в программном коде путем сопоставления фрагментов кода с образцами из базы данных шаблонов (сигнатур) дефектов безопасности. В зависимости от технологии, применяемой при сопоставлении фрагмента кода и шаблона, а также от промежуточного представления, могут использоваться как обычные алгоритмы поиска подстроки в строке, так и регулярные выражения, специальные языки запросов по структурированной информации, в частности XQuery для XML, или специально разработанные для этой задачи способы сопоставления. В [2] приведены примеры правил формирования сигнатур ошибок, соответствующих стандарту CWE. Признакам потенциально опасных конструкций в программных системах и методам оценки их безопасности посвящены статьи 6, 7].

Самый простой способ поиска потенциально опасных конструкций — применение регулярных выражений. Этот вариант достаточно прост в реализации (легко добавляются новые шаблоны уязвимостей), однако имеет несколько критических недостатков. В первую очередь к ним относится невозможность создать шаблоны для сложных дефектов, причем даже для простых дефектов кода регулярное выражение может занимать очень много места, а также очень низкая скорость поиска.

Более эффективным считается построение промежуточного представления кода и его анализ. Именно такой способ используется почти во всех современных анализаторах кода. Общее представление структуры статического анализатора показано на рис. 2.

Рис. 2. Структура статического анализатора

На начальном этапе решение задачи анализа исходного кода напоминает действия компилятора либо интерпретатора (в случае динамического языка). Исходные тексты разбиваются на лексемы, на основании которых впоследствии строится абстрактное синтаксическое дерево (Abstract syntax tree — AST) и в дальнейшем семантический граф (Abstract semantic graph — ASG). Далее AST и ASG анализируются механизмом, подобным поиску регулярных выражений в тексте, либо используются скрипты для анализа. Помимо этого, анализ графов выполнения и потоков данных (Data Flow analysis) может дать полезную информацию.

Примеры дефектов кода

Самые распространенные виды дефектов кода — ошибки программирования и злонамеренно внесенный код. Следует заметить, что не всякая ошибка программирования приводит к возникновению уязвимости, а только те, в результате эксплуатации которых страдают защищенность, целостность и доступность данных. Злонамеренно внесенный код, как правило, позволяет получить несанкционированный доступ к системе, но бывают и другие типы дефектов, приводящие, например, к удаленному выполнению кода, сбору и отправке конфиденциальной информации либо к реализации несанкционированных действий при наступлении определенного момента времени (time bomb).

К наиболее известным и опасным уязвимостям нужно отнести уязвимости следующих видов:

Отсутствие проверки вводимых данных

Уязвимость появляется, если не производится проверка данных, поступивших из поля ручного ввода данных; от пользователя ожидается простая информация, например имя, email, текст короткого сообщения. Если при этом в поле ввода дописать код специально сформированной команды SQL или Javascript, то данный код будет выполнен. Например:

$stmt = prepare_query(«SELECT * FROM users WHERE username =?»);

В данном примере каждый параметр передается через отдельный метод bind_param, который и осуществляет фильтрацию специальных символов и ключевых слов.

Ошибки работы с памятью

Чаще всего встречаются ошибки, приводящие к переполнению буфера. Они, как правило, возникают в программах на языках C и C++. Если программа получает данные извне и копирует их во внутренний буфер без проверки размера копируемой области памяти, то при получении достаточно большого объема сведений происходит запись передаваемых данных на место адреса возврата и данных других процедур в области памяти стека. Эта ошибка может быть использована злоумышленником, который подставит нужное значение на место адреса возврата таким образом, чтобы в процессе возврата из подпрограммы переход осуществлялся вовнутрь той же перезаписанной области данных, где можно разместить вредоносный код. Пример уязвимого кода:

int main(int argc, char *argv[]) <

strcpy(buf, argv[1]);
// копирование

// без проверки длины

Пример безопасного кода:

int main(int argc, char *argv[]) <

strncpy(buf, argv[1], sizeof(buf));

// копируется не больше

Ошибки реализации

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

Вредоносный код

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

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

Эффективность статического анализа

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

Использование AppСhecker [8] позволяет существенно упростить и сократить затраты на ревизию кода при разработке программ, а также повысить качество кода и сократить количество ошибок и уязвимостей еще на этапе проектирования, что также позитивно скажется на затратах на доработку и поддержку выпускаемого ПО.

Помимо основного функционала в любом программном обеспечении, взаимодействующем с пользователем, очень важно удобство его применения. Удобный и понятный интерфейс (рис. 3), интеграция в процесс разработки, простота установки, обновления и поддержки становятся важными характеристиками анализаторов кода. Тем не менее сделать анализатор полностью автоматическим, действующим по «нажатию одной кнопки», невозможно.

AppChecker

Рис. 3. Общий вид рабочего экрана AppChecker

Как уже упоминалось, работа анализатора во многом близка работе компилятора. И для того чтобы провести качественный анализ, требуются те же действия, которые выполняет система сборки: проанализировать зависимости в проекте, распознать и «подключить» библиотечные функции и т. д., что может потребовать дополнительных настроек процесса анализа. В сравнении с другими статическими анализаторами, AppСhecker предоставляет пользователям бо? льшую гибкость в настройке: он понятен простым пользователям, а опытные разработчики смогут интегрировать его в процесс сборки любой сложности.

Важной составляющей безопасности кода является применение проверенных сторонних компонентов — библиотек или готовых программ с открытым исходным кодом. Их можно проверить, сообщая авторам о найденных уязвимостях и создавая базу уязвимостей open-source библиотек. Это особенно актуально для поддерживаемых анализатором AppСhecker интерпретируемых языков PHP, Javascript, в которых программы хранятся в виде исходных текстов.

Заключение

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

Источники:

https://www. internet-technologies. ru/articles/10-sovetov-dlya-otladki-i-ustraneniya-nepoladok-v-programmirovanii. html

https://controlengrussia. com/programmnye-sredstva/bezopasnost-programmnye-sredstva/appchecker/

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

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