05 июля 2015

         Ловля багов
Alone Coder 

  Вдохновившись блогом PVS-Studio на Хаб─
ре, я решил попробовать найти самые типич─
ные ошибки, которые возникают при разрабо─
тке на ассемблере Z80.

   Хотелось  бы  верить, что  это  поможет
увеличить  эффективность  кодинга  на   ZX
Spectrum. Кроме  практических советов мож─ 
но, в конце концов, сделать и утилиты кон─
троля  исходников  (хотя бы и встроенные в
ассемблер), которые  найдут самые типичные
ошибки. Причём не в том смысле,что кодиро─
вать  как-то "нестандартно" будет ошибкой.
Просто  будут  выдаваться  предупреждения,
если подозрительный код не пометить как-то
особо. Предупреждения  давно показали себя
хорошим инструментом в компиляторах на бо─
лее других системах. Они позволяют, напри─
мер, найти  ошибку"=" вместо "==" в C или
какие-нибудь  триггеры  по  двум фронтам в
Верилоге (знающие люди поправят). 
   Уж не знаю, почему у нас так мало пишут
про  работу с ошибками - наверно, подразу─
мевается, что крутые кодеры не делают оши─
бок. (Как крутые  художники, которые якобы
рисуют без стёрки и сразу набело.) Это,ко─
нечно, неправда. Поиск  ошибок занимает во
всяком случае не меньше времени, чем напи─
сание кода.Но если работу построить верно,
то  можно  отладку  проводить без нервов и
почти её не замечать.

          И как же это сделать?

   Есть методы  доказательного  программи─
рования, описанные  в  умных книжках (типа
Robert L.Baber.Error-free Software. Know- 
how and Know-why of Program Correctness ).
Там функции (не выше  определённой сложно─
сти) проверяются хитроумными методами, но,
боюсь, не  для  нас с вами. Это хорошо для
тех случаев, когда программа уже на первом
запуске  должна работать как часы, а время
на разработку  выделено  с запасом  на все
доказательства и перепроверки (ибо доказа─
тельства  функций чуть ли не сложнее самих
функций).Но у нас другой случай - мы можем
запускать программу когда угодно и сколько
угодно, и она ничего не испортит.

   Разберём по ситуациям:

 а)надо написать новую программу;
 б)надо переделать свою программу;
 в)надо переделать чужую программу.

   Начнём са). Хорошо  писать  программу,
если  заранее знаешь всю её структуру. Сам
по себе  кодинг  занимает  мало времени по
сравнению с проектированием. Говорят, про─
фессиональный  программист  пишет 30 строк
отлаженного кода в день (были и более пес─
симистичные оценки, в том числе у всем из─
вестного Фредерика Брукса ). Если есть вся
структура и чётко ясно,какая процедура что
должна делать,то мы можем проверить каждую
процедуру по отдельности. Можно даже авто─
матизировать это функциональное тестирова─
ние,если мы собираемся программу регулярно
переделывать - тут  мы переходим кб). Это
хорошо, когда программа большая, разработ─
чиков  много  и за всем  не уследишь. Если
вместе  с каждой процедурой есть проверяю─
щая процедура, а перед каждой сборкой  все
эти  проверяющие  процедуры вызываются, то
можно контролировать, что никто ничего чу─
жого ненароком не сломал. Это ужев).

   Но опять-таки: методы хороши, но не для
наших задач.Мы можем пересобирать програм─
му сколько угодно и когда угодно, хоть по─
сле каждого изменения. И не просто можем,а
так и надо делать. Маленький шажок, компи─
ляция, запускаем,ловим баг - и баг с веро─
ятностью  95%  находится в новодобавленном
коде, даже не нужен отладчик.
   Но чтобы сделать этот шажок, нам нужно,
чтобы  исходный  вариант программы был сам
по  себе  работоспособным. Отсюда  следуют
выводы прямо по пунктама),б),в):
 а): в первую очередь при разработке про─
граммы  надо выстроить хоть какой-то рабо─
тоспособный скелет,который потом можно по─
степенно  менять. Например, ACEdit в деви─
честве был листалкой текстов под 48К, сна─
чала даже и без дисковых операций.
 б): прерывать разработку  программы надо
обязательно на работоспособной версии,ина─
че впоследствии её не поднимешь или подни─
мешь  с несоразмерно большим трудом. Жела─
тельно ещё и документировать, как её соби─
рать.Поленился документировать или сделать
автосборщик? Так умер проект Awaken.
 в): если  надо переделать чужую програм─
му, то сначала надо убедиться,что она ком─
пилируется  и работает без переделок. Если
эта переделка сопряжена с переносом в дру─
гой ассемблер или декомпиляцией,то для на─
чала надо проверить,что после такого пере─
носа всё компилируется в блок кода,побайт─
но идентичный старому.

   Сферический  алгоритм  такой  переделки
программ:
 1.Добиться, чтобы  компилировалось  (не
факт, что правильно). 
 2.Добиться, чтобы работало (в соответс─
твии со старым вариантом). 
 3.Добиться, чтобы  работало как надо (с
новыми изменениями). 
 4.Добиться, чтобы работало как надо по─
лностью (желателен бетатест). 
   При  переделке  исходников  со знакомых
систем и средств разработки первые два пу─
нкта выполняются уже в самом начале.

   Конкретно последовательность работы при
декомпиляции (говорю по своему опыту с Pro
Tracker 3 и Perfect Commander ): 
 1.Декомпилировать (с чётким разделением
кода и данных). Я использую ZXD. 
 2.Пройти по всему исходнику сверху вниз
и сделать заметки. 
 3.Расставить  нормальные метки и вычис─
ляемые выражения.Когда мы заменяем метку в 
процедуре, мы можем по сообщениям об ошиб─ 
ках узнать, сколько  к ней было обращений. 
Это можно отметить и потом  использовать в 
оптимизации. 
 4.Проверить, что  результат  компиляции
байт в байт  соответствует  образцу. Я ис─ 
пользую  утилиту  File Comparer  by MAYhEM 
(FCmp_v2BнаSYS.TRD). 
 5.Убедиться, что при добавлении в нача─
ло какого-нибудьDS 300 программа всё ещё 
работает. 
 6.Только потом начинать вносить измене─
ния (можно вIF'ах, через условную компи─ 
ляцию). 

                  * * *

   Итак, пусть мы научились работать мале─
нькими шажками. Выстраиваем скелет из зна─
комых процедур и знакомыми методами, потом
шаг  за шагом добавляем функционал, интер─
фейс, оптимизируем и т.д. Этакий Agile. Но
несмотря  на то, что 95% глюков  находятся
сразу (в том числе компилятором), остаётся
5% таких, над которыми надо поломать голо─
ву.
   Есть два основных способа ловли глюков:
 1.Всё проверить. А именно то, что изме─
нено, и зависящий  от этого  код. И заодно 
вызываемый оттуда код. 
 2.Сделать тесткейз (сиречь доломать).
   Я обычно пользуюсь вторым. Нахожу тест─
кейз, который  точно глючит (можно сделать
сцециальные условия прямо в программе,что─
бы выйти на ветку), его трассирую или тупо
смотрю  переменные.
   Например, мы  пишем  3D движок. Сделали
кое-как, крутим-вертим,и тут БАЦ - что это
промелькнуло? Как это повторить? А это за─
висит от того,как мы крутили. Если крутили
автоматически, то нужно только найти номер
кадра (например, трассируем кадр за кадром
и смотрим). Если с клавиатуры, то придётся
смотреть параметры и особо проверять подо─
зрительные комбинации (например,с нулевыми
углами, с максимумами  или минимумами мас─
штабов). Лично я предпочинаю крутить авто─
матически, пока не отлажу отрисовку. Упра─
вление с клавиатуры вставляю потом, только
для сложных траекторий и не всегда. Так, в
Critical Error, The Board  и  The Board II 
было управление с клавиатуры, а в  Mission
Highly Improbable и New View 48K были про─ 
писанные скрипты. Причём сначала я автома─
тически кручу, допустим, один полигон и по
одной оси (или двигая одну вершину),только
потом добавляю и проверяю другие движения,
комбинации полигонов и т.п. Чтобы  при бу─
дущих оптимизациях не развалить то,что уже
отлажено,я сохраняю эти старые тесты в ве─
тках  условной  компиляции. Если программа
внезапно  перестала работать, всегда можно
за  секунды  вернуться  и  проверить её на
простых случаях.

   Кстати. Один  добрый  дядя  (а может, и
злой) написал где-то в интернете, а я про─
цитирую  вам: "You  solve  a  problem, and
produce beautiful,understandable code.Then 
you  optimise and produce a big ugly chunk 
of WTF.Keep the original code in comments" 
("Вы решили задачу и получили красивый,по─
нятный  код. Потом вы его оптимизировали и
получили  огромный  кусок  непонятно чего.
Оставляйте  оригинальный код в комментари─
ях"). Я часто  пользуюсь  этим  принципом.
Составляю алгоритм на языке высокого уров─
ня (иногда даже отлаживаю на Delphi ),кла─
ду его в комментарии и по этим комментари─
ям пишу  оптимизированный код. Я делал так
и на Си,когда писал Billiard: иногда ужас,
который  сгенерил  SDCC, можно  ускорить в
разы ассемблерными вставками,но перед эти─
ми ассемблерными вставками  лучше бы оста─
вить в комментариях оригинальный код. Если
же  я изначально пишу  на ассемблере, то у
меня  есть текстовый документ, где в общих
чертах  расписан метод и несколько вариан─
тов его реализации. Если  я меняю один код
на другой, то  я делаю это  через условную
компиляцию, а потом,если понадобится чист─
ка, старый вариант тоже где-то сохраняю.
   Да,не пытайтесь реализовать сразу окон─
чательный метод.У вас ещё скелет программы
не  заработал, а  уже  "big  ugly chunk of
WTF", который  надо отладить целиком. Сна─ 
чала  пусть  заработает  как-то. Например,
через процедуру точки в ПЗУ.Через атрибуты
вместо чанков. Без текстурирования.С ветв─
лением проверками, а не по таблице. И т.д.
Тут можно ещё вспомнить,что "преждевремен─
ная оптимизация - корень всех зол".
   Я писал  цветные чанки для New View 48K
сразу целиком и без отладки. Алгоритм уло─
жился  в713 строк - казалось бы, немного.
В итоге  при первой же  попытке это запус─
тить мне пришлось найти и исправить19 ба─
гов, из которых 3 были ошибками проектиро─
вания (в том числе касающимися числа штри─
ховок).

   А самые  сложные глюки - это комбинации
двух и более.

    (о практике см. в следующей статье)



Other articles:


Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Similar articles:
Events 2 - an interview with the sting / hoax group (sg) and steep / x-time. (Ste), who was chief organayzerom ascii demoparty 2oo2.
Interview - An Interview with Nicholas Kezhevnikovym (NICK SOFT)
Advertising - advertising and announcements.
Humor - the stories of the brave.

В этот день...   21 November