Гайд Перевод Learncpp.com: раздел 3.3 // Стратегия отладки. На русском.

Dreammmless.
Эксперт
Статус
Оффлайн
Регистрация
19 Мар 2019
Сообщения
2,956
Реакции[?]
962
Поинты[?]
1K
При отладке программы в большинстве случаев подавляющее большинство вашего времени будет потрачено на то, чтобы найти, где на самом деле находится ошибка. Как только проблема обнаружена, остальные шаги (исправление проблемы и проверка того, что проблема была исправлена) часто тривиальны по сравнению с ней.

В этом уроке мы начнем изучать, как находить ошибки.

Поиск проблем с помощью проверки кода

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

Рассмотрим следующий фрагмент программы:
C++:
int main()
{
    getNames();   // просите пользователя ввести кучу имен
    sortNames();  // сортируете их в алфавитном порядке
    printNames(); // выводите отсортированный список имен

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

Однако, как программы становятся более сложными, найти вопросы по код инспекции становится все более сложным, а также.

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

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

Поиск проблем путем запуска программы

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

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


Воспроизведение проблемы

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

Вернемся к нашей аналогии с диспенсером для льда - скажем, однажды ваш друг говорит вам, что ваш диспенсер для льда не работает. Вы идете посмотреть на него, и он прекрасно работает. Как бы вы диагностировали проблему? Это было бы очень трудно. Однако, если бы вы действительно могли видеть, что проблема с диспенсером для льда не работает, тогда вы могли бы начать диагностировать, почему он не работает намного эффективнее.

Если проблема с программным обеспечением очевидна (например, программа выходит из строя в одном и том же месте каждый раз, когда вы ее запускаете), то воспроизведение проблемы может быть тривиальным. Однако иногда воспроизведение проблемы может быть намного сложнее. Проблема может возникнуть только на определенных компьютерах или в определенных обстоятельствах (например, когда пользователь вводит определенные входные данные). В таких случаях может оказаться полезным создание набора шагов воспроизведения. Шаги воспроизведения - это список четких и точных шагов, которые могут быть выполнены, чтобы вызвать повторение проблемы с высоким уровнем предсказуемости. Цель состоит в том, чтобы быть в состоянии заставить проблему повторяться как можно чаще, так что мы можем запускать нашу программу снова и снова и искать подсказки, чтобы определить, что вызывает проблему. Если проблема может быть воспроизведена 100% времени, это идеально, но менее чем 100% воспроизводимость может быть в порядке. Проблема, которая возникает только в 50% случаев, просто означает, что для диагностики проблемы потребуется вдвое больше времени, так как в половине случаев программа не будет показывать проблему и, следовательно, не будет вносить никакой полезной диагностической информации.

Нахождение проблемы

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

Здесь нам очень пригодится аналогия. Давай поиграем в хай-лоу. Я попрошу вас угадать число от 1 до 10. Для каждого предположения, которое вы сделаете, я скажу вам, является ли каждое предположение слишком высоким, слишком низким или правильным. Пример этой игры может выглядеть следующим образом:

Вы: 5
Я: слишком мало
Вы: 8
Я: слишком много
Вы: 6
Я: слишком мало
Вы: 7
Я: Верно


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

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

Часто то, что заставило нас заметить проблему, дает нам первоначальное предположение, которое близко к тому, где находится реальная проблема. Например, если программа не записывает данные в файл, когда это должно быть, то проблема, вероятно, находится где-то в коде, который обрабатывает запись в файл (да!). Затем мы можем использовать стратегию hi-lo, чтобы попытаться изолировать, где на самом деле находится проблема.

Например:
  • Если в какой-то момент нашей программы мы можем доказать, что проблема еще не возникла, это аналогично получению “слишком мало” результата hi-lo-мы знаем, что проблема должна быть где-то позже в программе. Например, если наша программа каждый раз падает в одном и том же месте, и мы можем доказать, что программа не разбилась в определенный момент выполнения программы, то сбой должен быть позже в коде.
  • Если в какой-то момент в нашей программе мы можем наблюдать неправильное поведение, связанное с проблемой, то это аналогично получению “слишком высокого” результата hi-lo, и мы знаем, что проблема должна быть где-то раньше в программе. Например, предположим, что программа печатает значение некоторой переменной x.вы ожидали, что она напечатает значение 2, но вместо этого она напечатала 8. Переменная x должна иметь неверное значение. Если в какой-то момент во время выполнения нашей программы мы видим, что переменная x уже имеет значение 8, то мы знаем, что проблема должна была возникнуть до этого момента.
Аналогия hi-lo не идеальна - мы также можем иногда удалять целые разделы нашего кода из рассмотрения, не получая никакой информации о том, является ли фактическая проблема до или после этого момента.

Мы покажем примеры всех трех этих случаев в следующем уроке.

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

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

Так как же нам “строить догадки"? Есть много способов сделать это. Мы начнем с некоторых простых подходов в следующей главе, а затем мы будем развивать их и исследовать другие в следующих главах.

Оригинальная статья -
Пожалуйста, авторизуйтесь для просмотра ссылки.
 
Сверху Снизу