Опишу три случая, которые запомнились на всю жизнь и с которыми пришлось достаточно долго провозиться.
Случай 1. Писал я как-то программу под ДОС на встроенном компьютере с 486 процессором – он использовался в одном устройстве и достаточно активно работал с различного рода «железом». Одной из задач у него была ретрансляция команд по RS232 с между двух портов (с одного устройства на другое), при этом некоторые из команд обрабатывались самим компьютером, да и он мог генерить «высоко приоритетные команды». В общем были там очереди с приоритетами на портах, а сами обработчики прерываний RS были написаны на ассемблере (остальная программа была на Borland C++ 3.1). Работал я в паре с другим разработчиком, который проектировал и программировал все остальной «железо». Итак, вечер перед отправкой «изделия» заказчику – у нас все отлажено и более-менее работает, а мы возимся с какой-то мелочевкой, в общем «допалировываем». И вдруг после исправления чего-то в коде Си перестает работать передача команд... Полностью. При этом исправленный кусок к передачи данных ни каким образом не относился. Просматриваю файлы – все нормально – никаких «подозрительных» мест не видно. Восстановление изменененого кода к прежнему состоянию — ничего не дает – передача команд не работает. Восстановление предыдущего ПО для «железа» (оно правилось параллельно) — так же не помогает. А со старой версией программы – все работает. В общем провозились мы с этой проблемой где-то час – два, пока я не сравнил построчно все файлы проекта, и не обнаружил, что в какой-то момент в обработчике прерывания RS исчезла одна строчка с ассемлерной командой – видимо при переключении окон в борланде я ее случайно удалил какой-то комбинацией клавиш... С одной стороны сейчас это звучит смешно – найти такую проблему пара минут, но тогда пришлось серьезно понервничать и выучить, что изменения могут быть не только в том коде, корый правишь, а и в любом другом месте проекта, даже который и «не трогал».
Случай 2. Случился с одним моим коллегой, но я принимал горячее участие в поиске «бага». Писал он программу для микроконтроллера на Си. И все было нормально, но вдруг одна функция перестала модифицировать одну переменную. Все остальное она делала как нужно, а вот одну переменную наотрез отказывалась модифицировать. Как мы это раскапывали – это отдельная история (отладчиком воспользоваться было нельзя и приходилось выводить промежуточные значения на спец. индикатор, ну и «дергать» ноги микроконтроллера для просмотра на осцилографе), но в результате выяснилось следующее: внутри одной функции, достаточно сложной, одна глобальная переменная должна была инкрементироваться, а она оставалась неизменной. При этом постепенно стала проявляться интересная картина – эта переменная внутри функции все-таки изменялась, но при выходе из функции она вдруг принимала исходное значение. Чего только не было испробовано и каких только предположений не было высказано – вплоть до ошибок в компиляторе. Но все оказалось намного банальнее – указанная глобальная переменная передавалась внутрь функции в качестве параметра (т.к. программист начитался умных книг и знал, что «использованиеглобальных переменных – зло» ), а внутри функции этот параметр был объявлен точно таким же именем, как и глобальная переменная... Из-за этого при беглом просмотре кода создавалось впечатление, что меняется глобальная переменная. Тем более, что она изменялась в конце достаочно длинной функции и именно туда было сосредоточено все внимание.
Случай 3. Самый «крышесносный» и интересный в моей жизни. Устанавливали мы как-то наше «изделие» у заказчика на заводе и адаптировали наш софт для интеграции его с роботами заказчика. Адаптация шла в основном в программе микроконтроллера нашего изделия. И вот в какой-то момент, добавив простейшее действие, что-то типа a += b + 100; мы обнаружили, что наше «изделие» перестало адекватно реагировать на команды робота. При этом частично реагировало нормально, а частично «сошло с ума» — мы даже описать это не смогли бы, настолько реакция оказалась «чудной». Убрали строчку – все нормально. Добавили – фигня. Изменили строчку c =b + 100; a += c; — ерунда, но по другому. Разнесли строчки – одну чуть выше по коду, другую чуть ниже – с роботом все восстановилось. Пожали плечами и вздохнули с облегчением – сдадим сегодня «изделие» заказчику. Вдруг обнаружили – «отвалилось» запоминание параметров во флэш-памяти. В общем не буду утомлять, но бились мы с этим несколько часов, пока не нашли какую-то комбинацию, когда все заработало (на самом деле там пришлось добавлять не одну строчку и подобная фигня была, можно сказать, практически с каждой строчкой). Но «изделие» сдали (и оно до сих пор там работает), а сами с недоумением поехали домой. Дома же, когда начали разбираться, то обнаружили следующее – после «заливки» программы в микроконтроллер в ней «портился» один байт. Изначально наш код по объему был чуть менее 64k, а во время доработок у заказчика перевалил через эту границу. Загрузчик же программы (утилита третьей фирмы) имел баг и в микроконтроллер на записывал последний байт в 64k блоке, вернее обнулял его.
Были еще случаи и с переполнением секундных счетчиков в 16 бит, и, как результат, снятия сигнала готовности на одну секунду раз в 18 минут (65536 / 3600). И поиск ошибки в программах системы, из-за чего длина участка материала, обрабатываемого нашим «изделиием» в некоторых случаях оказывалась меньше, чем заданное –причина оказалась в брызгах металла, которые постепенно нарастали в определенных местах на установке заказчика и создавали физическое препятствие . И поиск причин сбоев на конвейере, возникающих у заказчиков раз в неделю (причина оказалась в отсутствии блокировки прерываний при изменении пары переменных, которые всегда должны изменяться одновременно). Но это была «обычная» работа, которая не произвела очень сильного впечатления...