Кодеры за работой. Размышления о ремесле программиста | страница 9



Сейбел: Когда вы говорите, что не было нормальной документации, это значит, что документация была неточной или что ее не было вовсе?

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

Сейбел: Ну, такое случается сплошь и рядом, начиная от низкоуровневого системного программирования и заканчивая высокоуровневым API, когда всё начинает работать совсем не так, как ты ожидаешь, или не так, как написано в документации. Как вы справлялись с этим?

Завински: Да просто начинаешь ожидать этого. Чем раньше поймешь, что сбился с пути, тем раньше сможешь выяснить, где именно. Лично я пытался создать исполняемый файл. Я знал, что компилятор Си может создавать исполняемые файлы. Поэтому алгоритм работы был такой: берешь хороший исполняемый файл и начинаешь его ковырять, пока он не превратится в плохой. Это основной механизм обратной разработки (reverse engineering).

Думаю, именно в компании Lucid я исправил самый сложный компьютерный баг. Я дошел до момента выполнения исполняемого файла, когда тот пытался загрузить интерпретатор Лиспа, но после выполнения 500 инструкций процесс загрузки падал. Тогда я начал выполнять процесс загрузки пошагово, чтобы выяснить, где же он падает. Хотя это было бессмысленно, создавалось впечатление, что процесс падал каждый раз в другом месте. Я стал исследовать ассемблерный код компьютерной архитектуры, о которой имел лишь смутное представление. Наконец до меня дошло. “Господи, при пошаговом выполнении он делает что-то не то. Возможно проблема связана с временными задержками”. В итоге я понял, что происходило: дело в том, что это была одна из первых машин с упреждающим исполнением команд[6]. В этом случае выполнялись обе ветви кода[7]. Но GDB[8] при пошаговой отладке выполнял только одну из ветвей. Так что баг был в GDB.

Сейбел: Здорово.

Завински: Точно. Но это меня подкосило. “Господи! Мне придется отлаживать GDB, который я первый раз вижу”. Чтобы обойти ошибку отладчика, нужно остановить выполнение процесса перед инструкцией ветвления, задать точки останова в обеих ветвях и продолжить выполнение. Именно таким способом мне удалось воспроизвести ситуацию и понять, что же происходит на самом деле. Затем я потратил около недели на исправление GDB, но так и не смог понять, в чем же дело. Я предполагал, что из-за проблем с одним из регистров отладчик считал, что всегда выполняется одна из ветвей условия или что-то в этом роде. Поэтому я изменил команду пошагового выполнения инструкций, чтобы определить, когда оно дойдет до инструкции ветвления, и там сказать: “Стоп, это не делай”. Теперь я мог просто пошагово выполнять программу. Выполнение в конце концов останавливалось, я вручную задавал точку останова и продолжал выполнение. Когда что-то отлаживаешь, понимая, что не только путь выбран неверный, так еще и инструмент никуда не годится, - что может быть хуже.