Совершенный код. Совершенный код. Мастер-класс. Стив Макконнелл. Руководство по стилю программирования и конструированию по
Скачать 5.88 Mb.
|
ГЛАВА 23 Отладка 533 Далее вы подтверждаете эту гипотезу при помощи дополнительных тестов. Советы по поиску причин дефектов После стабилизации ошибки и уточнения вызывающих ее условий поиск ее может быть как тривиальным, так и трудным: это зависит от того, насколько хорошо на# писан код. Если найти причину дефекта не удается, возможно, это обусловлено низким качеством кода. Каким бы неприятным ни был такой вывод, ничего не по# делаешь: это правда. В подобной ситуации вам помогут следующие советы. Формулируя гипотезу, используйте все имеющиеся данные Выдвигая ги# потезу об источнике дефекта, постарайтесь учесть как можно больше данных. В нашем примере вы могли бы обратить внимание на неверное место записи Fruit% Loop, Frita и предположить, что неверно сортируются все фамилии, начинающи# еся на букву «F». Гипотеза неудачна: она не объясняет неправильное место записи Modula, Mildred и правильную сортировку записей при втором запуске програм# мы. Если данные не соответствуют гипотезе, не игнорируйте их — подумайте, почему они ей не соответствуют, и сформулируйте новую гипотезу. Вторая наша гипотеза, согласно которой причина проблемы связана с фамилия# ми, содержащими дефисы, а не с вводом фамилий по одной, также первоначаль# но не объясняла правильную сортировку фамилий при повторном выполнении программы. Однако она привела нас к более точной гипотезе, оказавшейся вер# ной. Если первая гипотеза не объясняет все данные, ничего страшного — улуч# шайте ее, пока не достигнете этой цели. Детализируйте тесты, приводящие к ошибке Если вы не можете найти ис# точник ошибки, попробуйте уточнить уже имеющиеся тесты. Возможно, измене# ние какого#нибудь из параметров в более широком диапазоне или концентрация на одном из параметров позволит сдвинуть отладку с мертвой точки. Проверяйте код при помощи блочных тестов Как пра# вило, в небольших фрагментах кода дефекты искать легче, чем в крупных интегрированных программах. Используй# те для изолированного тестирования фрагментов кода блоч# ные тесты. Используйте разные инструменты На рынке имеются многочисленные ин# струменты, облегчающие отладку: интерактивные отладчики, строгие компиляторы, инструменты проверки памяти, утилиты проверки синтаксиса и т. д. Правильный инструмент способен сделать трудную работу простой. Мне вспоминается случай, когда одна неуловимая ошибка заставляла одну часть программы перезаписывать память другой части. Традиционные методики отладки не помогли в поисках ошибки: программист не мог определить, какой именно фрагмент перезаписы# вал память. Тогда он установил точку прерывания на конкретный адрес памяти. Когда программа выполнила запись по этому адресу, отладчик прервал ее выпол# нение, и виновный код был обнаружен. Это пример проблемы, которую трудно диагностировать аналитически, но довольно легко с помощью правильного инструмента. Воспроизведите ошибку несколькими способами Иногда полезную инфор# мацию можно получить, выполнив тест, похожий на тесты, приводящие к ошиб# Перекрестная ссылка Среды блочного тестирования упоми- наются в подразделе «Встраи- вайте блочные тесты в среду тестирования» раздела 22.4. 534 ЧАСТЬ V Усовершенствование кода ке, но не идентичный им. Можете рассматривать этот подход как триангулирова# ние дефекта. Воспроизведя дефект несколькими способами, вы точнее определи# те его источник. Воспроизведение ошибки разными способами помогает диагностировать причину ошибки (рис. 23#1). Как только вы решили, что причина дефекта ясна, выполните тест, который сам не должен вызывать ошибку, но напоминает тесты, приводя# щие к ошибке. Если ошибка при этом все же возникает, значит, вы еще не полно# стью поняли проблему. Причиной ошибки часто становится комбинация факто# ров, поэтому попытка диагностировать проблему при помощи только одного те# ста часто не приводит к обнаружению корня проблемы. Рис. 23'1. Чтобы точно определить причину ошибки, попробуйте воспроизвести ошибку разными способами Генерируйте больше данных для формулирования большего числа гипотез Выберите тесты, отличающиеся от тестов, результаты которых уже известны. Выполните их, чтобы получить дополнительные данные и использовать их для выдвижения дополнительных гипотез. Используйте результаты отрицательных тестов Предположим, что вы выдвинули гипотезу и запустили тест с целью ее подтверждения. Допустим далее, что тест опроверг гипотезу, так что причина ошибки все еще неизвестна. И все же вы узнали нечто полезное: одно из ваших предположений о причинах дефек# та ошибочно. Это сужает область поиска и уменьшает число оставшихся гипотез. Используйте «мозговой штурм» для построения нескольких гипотез Не останавливайтесь на первой пришедшей в голову гипотезе, а попробуйте выдви# нуть несколько гипотез. Не анализируйте их сразу — просто придумайте за не# сколько минут максимальное число гипотез. Затем рассмотрите их по очереди и подумайте о тестах, которые могут их доказать или опровергнуть. Это упражне# ние помогает сдвинуть отладку с мертвой точки, обусловленной слишком силь# ной концентрацией на одной линии рассуждения. Составьте список подходов, которые стоит попробовать Иногда про# граммисты не могут найти ошибку по той причине, что слишком долго следова# ли по пути, ведущему в тупик. Составьте список подходов, которые стоит попро# бовать, и, если один из них не работает, переходите к следующему. ГЛАВА 23 Отладка 535 Сократите подозрительную область кода Вместо тестирования всей про# граммы, всего класса или метода протестируйте сначала меньший фрагмент кода. Используйте для нахождения ошибочного фрагмента команды печати, запись информации в журнал или трассировку. Есть и более эффективный способ сужения подозрительной области кода: систе# матически удаляйте части программы и смотрите, возникает ли ошибка. Если ошибка исчезла, ищите ее в удаленной части. Если ошибка по#прежнему возни# кает, дефектный код все еще присутствует в программе. Вместо удаления случайных фрагментов руководствуйтесь принципом «разделяй и властвуй». Используйте алгоритм двоичного поиска. Попробуйте удалить в пер# вый раз примерно половину кода. Определите половину, содержащую дефект, и разделите ее. Снова определите дефектную половину и снова разделите ее попо# лам. Продолжайте, пока дефект не будет найден. Если программа содержит много небольших методов, можете убирать фрагменты кода, просто комментируя вызовы методов. В противном случае можете блокиро# вать фрагменты кода при помощи комментариев или директив препроцессора. Работая с отладчиком, удалять фрагменты кода не обязательно. Вместо этого можно задать точки прерывания. Если отладчик позволяет пропускать вызовы методов, попробуйте найти дефектный код, пропуская выполнение определенных методов и наблюдая, исчезает ли после этого ошибка. Этот процесс во многом похож на действительное удаление фрагментов программы. С подозрением относитесь к классам и методам, ко' торые содержали дефекты ранее Классы, которые бы# ли дефектными раньше, более подвержены ошибкам. Новые дефекты чаще обнаруживаются в классах, с которыми и раньше были связаны проблемы, а не в классах, которые были безошибочными. Проанализируйте подверженные ошибкам классы и методы еще раз. Проверьте код, который был изменен недавно Если у вас появилась новая непростая ошибка, она скорее всего содержится в фрагменте, который был изме# нен недавно. Ее источником может быть как абсолютно новый код, так и изме# ненный старый. Если вы не можете найти дефект, запустите старую версию про# граммы и проверьте, возникает ли ошибка. Если нет, ошибка содержится в новом коде или объясняется взаимодействием с новым кодом. Изучите различия между старой и новой версиями. Посмотрите в журнале системы управления версиями, какой код был изменен недавно. Если это невозможно, используйте для сравне# ния старого работоспособного и нового дефектного кода другой инструмент. Расширьте подозрительный фрагмент кода Сосредоточиться на небольшом фрагменте кода легко, но это принесет пользу, только если дефект наверняка содержится в этом фрагменте. Если дефект не удается найти в конкретной обла# сти кода, рассмотрите вероятность того, что его в ней нет. Расширьте подозри# тельную область кода и проанализируйте ее, применив описанную выше методи# ку двоичного поиска. Перекрестная ссылка О коде, подверженном ошибкам, см. также подраздел «Выполняйте рефакторинг модулей, подвер- женных ошибкам» раздела 24.5. 536 ЧАСТЬ V Усовершенствование кода Выполняйте интеграцию инкрементно Отладка будет легкой, если вы будете добавлять элементы в систему по од# ному за раз. Если после добавления нового элемента возникла новая ошибка, удалите его и протестируйте отдельно. Проверяйте наличие распространенных дефектов Размышляя о возможных дефектах, используйте контрольные списки качества кода. Следуя методикам ин# спекций (см. раздел 21.3), вы создадите улучшенные контрольные списки проблем, характерных для вашей среды. Вы также можете использовать контрольные спис# ки, приведенные в этой книге. Все они перечислены после содержания книги. Обсудите проблему с кем'то другим Некоторые про# граммисты называют это «отладочной исповедью». Довольно часто дефект в своем коде можно найти при объяснении его другому человеку. Так, объясняя проблему с неверной сорти# ровкой фамилий, вы могли бы сказать что#нибудь подобное: Привет, Дженифер, у тебя есть свободная минутка? У меня проблема. Вот этот список сотрудников должен быть отсортирован, но не% которые фамилии выводятся в неверном порядке. Однако во второй раз они сортируются правильно. Чтобы узнать, не связана ли проблема с вводом но% вых фамилий, я добавил несколько новых записей, но все они были выведены правильно. Я знаю, что список должен быть отсортирован уже при первом запуске программы, потому что фамилии сортируются дважды: при вводе и при сохранении… подожди… нет, при вводе они не сортируются. Все верно. При вводе их место в списке определяется лишь приблизительно. Спасибо, Джени% фер, ты мне очень помогла. Дженифер не сказала ни слова, но вы нашли решение проблемы! Отдохните от проблемы Иногда чрезмерная концентрация на проблеме ме# шает думать. Помните, как вы решили сделать перерыв на чашку кофе и нашли ре# шение проблемы на пути к кофейному автомату? Или во время обеда? Или по пути домой? Или принимая душ следующим утром? Если, попробовав все варианты, вы не получили никаких результатов, отдохните. Прогуляйтесь. Поработайте над чем# то другим. Возьмите выходной. Пусть проблемой займется ваше подсознание. Дополнительная выгода временного отдыха от проблемы состоит в том, что он снижает связанную с отладкой тревогу. Приступ беспокойства — явный признак того, что пора сделать перерыв. Отладка методом грубой силы При отладке этот способ часто игнорируют. Под «методом грубой силы» я пони# маю подход, который может оказаться нудным, трудным и длительным, но непре% менно приведет к решению проблемы. Какие именно подходы непременно при# ведут к решению проблемы? Это зависит от ситуации, но некоторые типичные варианты назвать можно: полный обзор проекта и/или кода дефектного фрагмента; выбрасывание фрагмента кода и его повторное проектирование/кодирование с нуля; Перекрестная ссылка Обраще- ние за помощью к коллегам иногда помогает рассмотреть проблему под другим углом зрения (см. раздел 21.1). Перекрестная ссылка Об инте- грации см. главу 29. ГЛАВА 23 Отладка 537 выбрасывание всей программы и ее повторное проектирование/кодирование с нуля; компиляция кода с полной отладочной информацией; компиляция кода на самом строгом уровне диагностики и исправление всех предупреждений компилятора; блочное тестирование нового кода в изоляции от других фрагментов; создание и выполнение набора автоматизированных тестов; пошаговое выполнение крупного цикла в отладчике вплоть до возникновения ошибки; включение в код команд печати, вывода информации на экран или других команд регистрации данных об ошибке; компиляция кода с использованием другого компилятора; компиляция и выполнение программы в другой среде; компоновка кода со специальными библиотеками или выполнение кода в сре# дах, генерирующих предупреждения в подозрительных ситуациях; полное воспроизведение конфигурации компьютера конечного пользователя; интеграция нового кода в систему небольшими фрагментами с полным тес# тированием каждого фрагмента. Установите лимит времени для быстрой и грязной отладки Рассматри# вая тот или иной метод грубой силы, вы вполне можете решить «Я не могу пойти на это — слишком много работы!» Однако слишком большим можно считать только тот объем работы, который требует больше времени, чем то, что я называю «бы# строй и грязной отладкой». Мало кому хочется методично анализировать весь код до тех пор, пока дефекту больше негде будет деться, — всегда есть соблазн быст# ро угадать причину проблемы. Игрок, живущий в каждом из нас, скорее выбрал бы рискованный подход, позволяющий обнаружить дефект за пять минут, а не надежную методику, непременно приводящую к нахождению дефекта за полчаса. Однако это рискованное дело: если пятиминутный подход не работает, вы начи# наете упрямиться. Обнаружение дефекта «легким» способом становится делом принципа, и вы впустую тратите часы, а потом дни, недели, месяцы… Как часто вы тратили два часа на отладку кода, на написание которого уходило только 30 минут? Такое распределение времени трудно признать разумным, и вам следова# ло бы не отлаживать плохой код, а вообще переписать его. Если вы все же решаете попробовать быстрый способ отладки, ограничьте его применение определенным интервалом времени. По истечении этого срока сми# ритесь с тем, что дефект не так прост, как вам казалось сначала, и используйте трудный путь. Так вы сможете быстро избавляться от простых дефектов, а исправ# ление сложных будет чуть более долгим. Составьте список методик грубой силы До начала отладки сложной ошибки спросите себя: «Есть ли какой#нибудь способ, который непременно приведет к решению этой проблемы, если при ее отладке я зайду в тупик?» Если вы опреде# лите хотя бы один такой способ (пусть даже переписывание дефектного кода!), вероятность того, что вы впустую потратите лишние часы или дни, уменьшится. 538 ЧАСТЬ V Усовершенствование кода Синтаксические ошибки Синтаксические ошибки ждет судьба мамонтов и саблезубых тигров. Диагности# ческие модули компиляторов постоянно улучшаются, и времена, когда поиск не# правильно расположенной точки с запятой иногда занимал несколько часов, по# чти ушли. Соблюдение следующих принципов поможет вам ускорить вымирание подобных ошибок. Не полагайтесь на номера строк в сообщениях компилятора Если ком# пилятор сообщил о загадочной синтаксической ошибке, изучите фрагменты, рас# положенные прямо перед ошибкой и сразу после нее: возможно, компилятор неправильно понял проблему или просто включает плохой диагностический модуль. Обнаружив истинный дефект, попробуйте определить, почему компиля# тор указал не на ту команду. Понимание особенностей компилятора поможет находить дефекты в будущем. Не доверяйте сообщениям компилятора Компиляторы пытаются сообщить вам точную причину ошибки, но они сами нередко ошибаются, и, чтобы понять смысл их сообщений, иногда приходится читать между строк. Так, при целочис# ленном делении на 0 компилятор UNIX C может вывести сообщение «floating exception» (исключение при выполнении операции над числом с плавающей точ# кой). Используя Standard Template Library C++, можно получить пару сообщений об ошибке: первое — о действительной ошибке в использовании STL, а второе — «Error message too long for printer to print; message truncated» («Сообщение об ошибке сокращено, так как оно слишком велико для печати»). Наверное, вы и сами може# те привести массу примеров неверных сообщений об ошибках. Не доверяйте второму сообщению компилятора Одни компиляторы нахо# дят множественные ошибки лучше, а другие хуже. Обнаружив первую ошибку, некоторые компиляторы приходят в такое возбуждение, что выдают десятки бес# смысленных сообщений о других ошибках. Другим компиляторам, более рассу# дительным, тоже нравится находить ошибки, но они воздерживаются от вывода неверных сообщений. Если ваш компилятор сгенерировал ряд сообщений об ошибках и вы не можете быстро найти причину второго или третьего сообще# ния, не волнуйтесь — исправьте первую ошибку и перекомпилируйте программу. Разделяй и властвуй Разделение программы на части особенно эффективно при поиске синтаксических ошибок. Если вы столкнулись с неуловимой синтаксичес# кой ошибкой, удалите часть кода и перекомпилируйте программу. Ошибка или ис# чезнет (следовательно, она содержится в удаленном коде), или снова появится во всей своей красе (в этом случае удалите другую часть кода). Кроме того, вы можете получить другое сообщение об ошибке (это значит, что вы перехитрили компиля# тор и заставили его сгенерировать более разумное сообщение). Грамотно ищите неверно размещенные комментарии и кавычки Многие текстовые редакторы для программи# рования автоматически форматируют комментарии, стро# ковые литералы и другие синтаксические элементы. В бо# лее примитивных средах неправильное размещение симво# лов комментария или кавычек может запутать компилятор. Перекрестная ссылка Наличие редакторов с проверкой синтак- сиса зависит от зрелости сре- ды программирования (см. раз- дел 4.3). |